INTRODUCTION

Where is development going to happen? Development is critical to accommodate growth and change, to provide housing, infrastructure, amenities, employment, and community spaces for economic prosperity and affordable shelter. But development has its downsides, too. When investments in neighborhoods drive up prices and the cost of living, it can – either directly or indirectly – force out long-term residents who are more vulnerable to cost changes. Understanding how economic development can equitably benefit residents rather than just push them to the urban periphery can help ensure that urban policy benefits city’s most vulnerable residents. Understanding patterns of and predicting development is key to this, and it’s challenging.

A key to understanding patterns of development is understanding endogenous gentrification. Endogenous gentrification describes the in-migration of wealthier residents to adjacent, poorer neighborhoods, causing prices to rise and lower-income residents to be priced out. We incorporate endogenous gentrification as well as other indicators of gentrification, neighborhood amenities and infrastructure to predict which areas of Philadelphia are most likely to experience new development. If tenants can understand how far into the future development pressure is likely to provide gentrification pressure and where, they can better target anti-displacement strategies. This application will help non-profit and volunteer-based housing justice organizations maximize tenant organizing capacity and resources for anti-displacement by directing policy makers where to invest housing trust funds & community land trusts.

Use Case

Community land trusts are an often-cited strategy for preventing displacement. A community land trust is a long-term strategy to preserve affordable housing by putting the land of property in a community-controlled trust and only selling the structure. By separating the land and property from the structure (as well as imposing deed restrictions for affordability including a predetermined re-sale formulate) guarantees affordability over the long term. Affordability measure can help low-income residents build equity through homeownership and retain affordable housing for future residents. However, community land trusts lack sufficient funding to scale up to meet affordable housing needs when demand is high across the board With limited resources and widespread need, targeting land trust funding is critical to prevent displacement.

Source: Virginia Statewide Community Land Trust, https://www.vsclt.org/faq/
Source: Virginia Statewide Community Land Trust, https://www.vsclt.org/faq/

Website Mockup

include photos of our hypothetical website application that could be used as a reuslt of this project.

METHODOLOGY

Using Philadelphia permit data as a case study, we created a Poisson model to predict where development is most likely to occur. We developed a spatial lag for Philadelphia neighborhoods to predict new construction permit issuance. One study examined Washington D.C., weighing permit patterns as potential indicators of development. This study identified and assigned weights to the following permit types: - Addition/Addition, Alteration, and Repair/Alteration and Repair - Electric/Electric General/Electric Heavy Up - Plumbing/Plumbing and Gas - Raze - New Building - Demolition - Mechanical - Certificate of Occupancy

We examined the Philadelphia permit dataset and filtered for residential construction permits. We then looked at population level data indicators of gentrification such as vacancy, residents with bachelor’s degrees, changes in rents, changes in home values, and median income to assign an indicator to each Census tract. In areas with decreasing vacancy and increasing bachelor’s degrees & incomes, we have used these as gentrification indicators independent of development. We then examined which permit types are most strongly associated with gentrification or intense gentrification, particularly where there is a large difference in the mean number of permits in gentrifying areas as opposed to non-gentrifying or ineligible areas (where income and home values are already high).

We engineered other features including proximity to desirable amenities, such as public transit, cafes, and jobs. We create spatial and temporal lags for many of our demographic variables, looking at the values of neighboring census tracts and the values from the prior (2017) 5-year American Community Survey.

We then run six Poisson linear regressions using two different sets of variables and three cross-validation strategies. We examine the errors in detail to identify the strongest model.

DEPENDENT VARIABLE

In this section, we investigate the dependent variable prior to any filtering.

Our dependent variable is the number of permits issued per census tract. The unit of analysis is permits per census tract because of census data availability and the fact that federal and state funding often flows to areas via census tract or zip code eligibility. We time and or space to see if there there are any patterns to missing permit data so as to better determine which years to test and train on.

# See if there are any patterns to missing spatial data

res_permits %>%
  group_by(year = year(as.Date(permitissuedate))) %>%
  summarise_all(funs(sum(is.na(.)))) %>%
datatable(options = list(pageLength = 12),
            style = "auto")

When looking at council district vs. year distribution of permit data, there are plenty of permits in each district, but wide differences by year. Permitting activity in earlier years is much lower than 2020 to 2023. Although 2020 is typically considered an anomaly, permit issuance and development was strong due to low interest rates. Therefore, we trained 2020 and 2021 permit issuance data to test on 2022 development activity. As a result, we got rid of missing spatial data

res_permits %>%
  group_by(council_district) %>%
  summarise(count = n()) %>%
  ggplot(aes(y = as.factor(council_district), x = count)) +
  geom_bar(stat = "identity", fill = "#ffb600") + 
  plotTheme() + 
  labs(title = "Permits by Council District", caption = "Figure 1")

res_permits %>%
  group_by(year = year(as.Date(permitissuedate))) %>%
  st_drop_geometry() %>%
  summarise(count = n()) %>%
  ggplot(aes (x = year, y = count)) +
  geom_bar(stat = "identity", fill = "#ffb600") +
  plotTheme() + 
  labs(title = "Permits by Year", caption = "Figure 2")

res_permits <- res_permits %>%
  filter(!is.na(lng) & !is.na(lat)) %>%
  st_as_sf(coords = c("lng","lat"),crs=4326)

Next, we examined permit types and counts to understand the distribution of permit types specific to Philadelphia. When looking at the count of permit types, as represented below by n, we can see that the permit type alone does not indicate what type of work is being done. New construction will necessitate different permit types, such as plumbing, mechanical, or electrical permits, but these can also be counted in regular maintenance. Therefore, filtering only based on new construction may be misleading. We looked at census data next to begin understanding how permit data is associated with demographics and our gentrification indicator.

res_permits %>%
  group_by(permitdescription, typeofwork) %>%
  st_drop_geometry() %>%
  summarise(n = n()) %>%
  arrange(permitdescription, desc(n)) %>%
  datatable(options = list(pageLength = 10),
            style = "auto")
phl_tracts4326 <- tracts(state = "PA",
                     county = "Philadelphia",
                     year = "2020") %>%
  select(GEOID, geometry) %>%
  st_set_crs(4326)

res_permits2 <- res_permits %>%
  group_by(permitdescription, typeofwork) %>%
  mutate(n = n()) %>%
  ungroup() %>%
  select(permittype, permitdescription, typeofwork, geometry, permitissuedate) %>%
  #join to census tract
  st_join(phl_tracts4326)

When we look at number of permits by type per tract as seen below, it becomes apparent there are a very high concentration of all permits in the Fishtown, Port Richmond area. When we look by year, we see that some of this is because of data unavailability in earlier years followed by a flux of permit issuance from 2020 to 2022.

So what happened these years?

  • Interest rates were at a historical low in 2020 to 2022, in part to entice economic activity to be as enticing as possible after the COVID-19 pandemic brought the economy to a screeching halt. Average interest Rates 2017 - 2023, Source: https://take-profit.org/en/statistics/interest-rate/united-states/

  • More locally, Philadelphia passed a tax policy in 2000 to provide a tax abatement on new housing construction until it expired in 2022. The policy goal was to spur development for residential property owners by providing a 10-year tax abatement on the value of improvements. This partially explains the time lag of some development, but it remains unclear why the development was so concentrated spatially.

Our next data analysis step is to drop the year and look only at permits in and after 2018. It is likely that more permits are issued in areas where these changes are already happening.

# PICK 1

tract_permits_year <- res_permits2 %>%
  st_drop_geometry() %>%
  mutate(permit = paste(permitdescription, "-", typeofwork),
         year = year(as.Date(permitissuedate))) %>%
  select(-permitissuedate, -permitdescription, -permittype, -typeofwork) %>%
  gather(Variable, value, -GEOID, -year) %>%
  dplyr::count(Variable, value, GEOID, year) %>%
  group_by(GEOID, year) %>%
  mutate(TOTAL = sum(n)) %>%
  ungroup() %>%
  spread(key = value, value = n) %>%
  select(-Variable) %>%
  replace(is.na(.), 0)

#total permits
tract_permits_year %>%
  left_join(phl_tracts4326) %>%
  st_sf() %>%
  filter(year > 2014 & year < 2023) %>%
  ggplot()+
  geom_sf(aes(fill = cut(TOTAL, breaks = 8))) +
  scale_fill_manual(values = palette1) +
  mapTheme() +
  labs(title = "Permits between 2014 - 2023", caption = "Figure 3")

#by year - there are a lot of tracts missing in earlier years
# tract_permits_year %>%
#   left_join(phl_tracts4326) %>%
#   st_sf() %>%
#   filter(year > 2014 & year <= 2023) %>%
#   ggplot()+
#   geom_sf(aes(fill = TOTAL)) +
#   scale_fill_binned(n.breaks=8) +
#   facet_wrap(~year) +
#   mapTheme()
tract_permits <- res_permits2 %>%
  st_drop_geometry() %>%
  mutate(permit = paste(permitdescription, "-", typeofwork),
         year = year(as.Date(permitissuedate))) %>%
  filter(year >= 2018) %>%
  select(-permitissuedate, -permitdescription, -permittype, -typeofwork, -year) %>%
  gather(Variable, value, -GEOID) %>%
  dplyr::count(Variable, value, GEOID) %>%
  group_by(GEOID) %>%
  mutate(TOTAL = sum(n)) %>%
  ungroup() %>%
  spread(key = value, value = n) %>%
  select(-Variable) %>%
  replace(is.na(.), 0)

PERMIT SELECTION

Census Data

But a key consideration is: does demand lead supply or vice versa? Which comes first, development or gentrification?

There are two theories of neighborhood change as it relates to this. - 1) Housing demand of middle-class, mostly white residents encourages them to leave neighborhoods to seek out more affordable neighborhoods. - 2) Development supply of new construction in historically poorer neighborhoods encourages an influx of new residents, who are encouraged to move to a neighborhood by the supply of housing.

The above study found that housing market growth (i.e. new permit issuance) did not predict future increase in gentrification; rather, development followed neighborhood change. Gentrification features themselves could be considered indicators of future development.

As a result of permit analysis, we selected permits that were associated with gentrification indicators. Using prior research on gentrification indicators and using publicly available census data, we examined the following demographic characteristics:

  • All Census tracts in Philadelphia, 5Y ACS for 2021 and for 2016
  • Median HH income, CPI-inflation adjusted
  • Median home value, CPI-inflation adjusted
  • Median rent, CPI-inflation adjusted
  • Median year structure built
  • Tenure - owner-occupied
  • Number of housing units
  • Number of vacancies
  • Count of second mortgages
  • Percent white population
  • Percent with Bachelor’s degree

We calculated: rent to income ratio, net changes of multiple indicators from 2017 to 2022, and percent changes from 2017 to 2022. Median rents, values, and incomes were adjusted to 2022 dollars.

CPI2017 <- 242.839
CPI2022 <- 281.148

CPI <- CPI2022/CPI2017

# NOTE: currently replacing NA values with a very small value...

phlCensus22 <- 
  get_acs(geography = "tract", 
          variables = c("B01003_001", "B19013_001", 
                        "B02001_002", "B25077_001",
                        "B25064_001", "B25035_001",
                        "B25003_001", "B25003_002",
                        "B25001_001", "B25002_001",
                        "B25002_003", "B25081_001",
                        "B25081_004", "B15003_001",
                        "B15003_022"
                        ), 
          year = 2022, 
          dataset = "acs5",
          state = "PA", 
          geometry = FALSE, 
          county = c("Philadelphia"),
          output = "wide") %>%
  rename(Total_Pop =  B01003_001E,
         Med_Inc = B19013_001E,
         White_Pop = B02001_002E,
         Med_Value = B25077_001E,
         Med_Rent = B25064_001E,
         Med_Structure = B25035_001E,
         Owner_univ = B25003_001E,
         Owners = B25003_002E,
         Tot_Units = B25001_001E,
         Vac_univ = B25002_001E,
         Vacants = B25002_003E,
         Mort_status_univ = B25081_001E,
         Sec_Mort = B25081_004E,
         Bach_univ = B15003_001E,
         Bach = B15003_022E
         ) %>%
  dplyr::select(Total_Pop, Med_Inc, White_Pop,
                Med_Value, Med_Rent, Med_Structure, 
                Owner_univ, Owners, Tot_Units,
                Vac_univ, Vacants, Mort_status_univ, 
                Sec_Mort, Bach_univ, Bach,  GEOID) %>%
  mutate(Percent_White = White_Pop / Total_Pop,
         Percent_Owner = Owners / Owner_univ,
         Percent_Vacant = Vacants / Vac_univ,
         Percent_2mort = Sec_Mort / Mort_status_univ,
         Percent_bach = Bach / Bach_univ,
         Rent_Income_Ratio = Med_Rent / (Med_Inc/12)
         ) %>%
  #replace(is.na(.), .00000001) %>%
  select(-ends_with("_univ"))

phlCensus17 <- 
  get_acs(geography = "tract", 
          variables = c("B01003_001", "B19013_001", 
                        "B02001_002", "B25077_001",
                        "B25064_001", "B25035_001",
                        "B25003_001", "B25003_002",
                        "B25001_001", "B25002_001",
                        "B25002_003", "B25081_001",
                        "B25081_004", "B15003_001",
                        "B15003_022"
                        ), 
          year = 2017, 
          state = "PA", 
          geometry = FALSE, 
          county=c("Philadelphia"),
          output = "wide") %>%
  rename(Total_Pop =  B01003_001E,
         Med_Inc = B19013_001E,
         White_Pop = B02001_002E,
         Med_Value = B25077_001E,
         Med_Rent = B25064_001E,
         Med_Structure = B25035_001E,
         Owner_univ = B25003_001E,
         Owners = B25003_002E,
         Tot_Units = B25001_001E,
         Vac_univ = B25002_001E,
         Vacants = B25002_003E,
         Mort_status_univ = B25081_001E,
         Sec_Mort = B25081_004E,
         Bach_univ = B15003_001E,
         Bach = B15003_022E
         ) %>%
  dplyr::select(Total_Pop, Med_Inc, White_Pop,
                Med_Value, Med_Rent, Med_Structure, 
                Owner_univ, Owners, Tot_Units,
                Vac_univ, Vacants, Mort_status_univ, 
                Sec_Mort, Bach_univ, Bach, GEOID) %>%
  mutate(
    Med_Rent = Med_Rent * CPI,
    Med_Value = Med_Value * CPI,
    Percent_White = White_Pop / Total_Pop,
         Percent_Owner = Owners / Owner_univ,
         Percent_Vacant = Vacants / Vac_univ,
         Percent_2mort = Sec_Mort / Mort_status_univ,
                  Percent_bach = Bach / Bach_univ,
         Rent_Income_Ratio =  Med_Rent / (Med_Inc/12),
         ) %>%
  #replace(is.na(.), .00000001) %>%
    select(-ends_with("_univ"))

# Interpolate based on population in order to translate 2017 tracts to 2020 tracts
# workaround because tidycensus wasn't downloading geometry

#get just population from phlCensus
pop_22 <- phlCensus22 %>%
  select(GEOID, Total_Pop)

#get tracts; join population to tracts
phl_tracts22 <- tracts(state = "PA",
                     county = "Philadelphia",
                     year = "2022") %>%
  left_join(pop_22) %>%
  st_sf()

#join tracts to Census data
phlCensus22 <- phlCensus22 %>%
  left_join(phl_tracts22 %>%
              select(GEOID, geometry)) %>%
  st_sf()

#get 2017 tracts
phl_tracts17 <- tracts(state = "PA",
                     county = "Philadelphia",
                     year = "2017")

#join tracts to 2017 Census data
phlCensus17 <- phlCensus17 %>%
  left_join(phl_tracts17 %>%
              select(GEOID,geometry)) %>%
  st_sf()

sf_use_s2(FALSE)

census17_interpolate_pw <- interpolate_pw(
  phlCensus17,
  phlCensus22,
  to_id = "GEOID",
  extensive = TRUE, 
  weights = phl_tracts22,
  weight_column = "Total_Pop",
  crs = 4269)

census_22_and_comparison <- phlCensus22 %>%
  left_join(st_drop_geometry(census17_interpolate_pw), 
            by = "GEOID",
            suffix = c("_2022", "_2017")) %>%
  mutate(Total_Pop_NET = Total_Pop_2022 - Total_Pop_2017, 
         Med_Inc_NET = Med_Inc_2022 - Med_Inc_2017, 
         White_Pop_NET = White_Pop_2022 - White_Pop_2017,
         Med_Value_NET = Med_Value_2022 - Med_Value_2017, 
         Med_Rent_NET = Med_Rent_2022 - Med_Rent_2017, 
         Owners_NET = Owners_2022 - Owners_2017, 
         Tot_Units_NET = Tot_Units_2022 - Tot_Units_2017,
         Vacants_NET = Vacants_2022 - Vacants_2017, 
         Sec_Mort_NET = Sec_Mort_2022 - Sec_Mort_2017,
         Percent_White_NET = Percent_White_2022 - Percent_White_2017,
         Percent_Owner_NET = Percent_Owner_2022 - Percent_Owner_2017,
         Percent_Vacant_NET = Percent_Vacant_2022 - Percent_Vacant_2017,
         Percent_2mort_NET = Percent_2mort_2022 - Percent_2mort_2017,
         Rent_Income_Ratio_NET = Rent_Income_Ratio_2022 - Rent_Income_Ratio_2017,
         Bach_NET = Bach_2022 - Bach_2017,
         Percent_bach_NET = Percent_bach_2022 - Percent_bach_2017,
         
         Total_Pop_PCT = ifelse(Total_Pop_2017>0, (Total_Pop_NET / Total_Pop_2017), NA)*100, 
         Med_Inc_PCT = ifelse(Med_Inc_2017 >0, (Med_Inc_NET / Med_Inc_2017), NA)*100, 
         White_Pop_PCT = ifelse(White_Pop_2017 >0, (White_Pop_NET / White_Pop_2017), NA)*100,
         Med_Value_PCT = ifelse(Med_Value_2017>0, (Med_Value_NET / Med_Value_2017), NA)*100, 
         Med_Rent_PCT = ifelse(Med_Rent_2017>0, (Med_Rent_NET / Med_Rent_2017),NA)*100, 
         Owners_PCT = ifelse(Owners_2017>0, ( Owners_NET / Owners_2017),NA)*100, 
         Tot_Units_PCT = ifelse(Tot_Units_2017>0, (Tot_Units_NET / Tot_Units_2017),NA)*100,
         Vacants_PCT = ifelse(Vacants_2017>0,(Vacants_NET / Vacants_2017),NA)*100, 
         Sec_Mort_PCT = ifelse(Sec_Mort_2017>0,(Sec_Mort_NET / Sec_Mort_2017),NA)*100,
         Percent_White_PCT =ifelse(Percent_White_2017>0,(Percent_White_NET / Percent_White_2017),NA)*100,
         
         # is it bad to calculate percent change using percentages?
         
         Percent_Owner_PCT = ifelse(Percent_Owner_2017>0,(Percent_Owner_NET / Percent_Owner_2017),NA)*100,
         Percent_Vacant_PCT = ifelse(Percent_Vacant_2017>0,(Percent_Vacant_NET / Percent_Vacant_2017),NA)*100,
         Percent_2mort_PCT = ifelse(Percent_2mort_2017>0,(Percent_2mort_NET / Percent_2mort_2017),NA)*100,
         
         Rent_Income_Ratio_PCT = ifelse(Rent_Income_Ratio_2017>0, (Rent_Income_Ratio_NET / Rent_Income_Ratio_2017),NA)*100,
         
         Percent_bach_PCT = ifelse(Percent_bach_2017>0, (Percent_bach_NET / Percent_bach_2017),NA)*100) %>%
  
  relocate(GEOID)

Gentrification Indicator

We developed a gentrification indicator methodology based on a Drexel study. This is just one possible indicator and could be improved upon or changed by incorporating additional demographic variables.

These four gentrification categories we use to characterize tracts are:

  • Ineligible
  • Not gentrifying
  • Gentrifying
  • Intensely Gentrifying

We can then see for which characterizations there are differences in permit types. If the mean number of permits for census tracts that are gentrifying or intensely gentrifying is larger than for tracts that are ineligible or not gentrifying, we keep that permit type.

indicator <- census_22_and_comparison %>%
  mutate(indicator = 
           case_when(
             
             # tracts with < 50 people or in top quartile of median income are excluded
             
             Total_Pop_2022 <= 50 | Med_Inc_2017 > quantile(Med_Inc_2017, .75, na.rm = TRUE) ~ "Ineligible",
         
             # tracts with below median increase in pct with bachelor's degree,
             # below median changes in rent and home values are not gentrifying
             Percent_bach_PCT < median(Percent_bach_NET, na.rm = TRUE) | 
               (Med_Value_NET < median(Med_Value_NET, na.rm = TRUE) &
                  Med_Rent_NET < median(Med_Rent_NET, na.rm = TRUE)) ~ "Not gentrifying",
         
            # tracts with above-median percent increases in bachelor degree attainment
            # and EITHER b/t .5 and .75 percentile increase in home values or rents = gentrifying
         
         Percent_bach_PCT >= median(Percent_bach_NET, na.rm = TRUE) & (
           (Med_Value_NET >= quantile(Med_Value_NET, .50, na.rm = TRUE) &
              Med_Value_NET < quantile(Med_Value_NET, .75, na.rm = TRUE)) |
             (Med_Rent_NET >= quantile(Med_Rent_NET, .50, na.rm = TRUE) & 
                Med_Rent_NET < quantile(Med_Rent_NET, .75, na.rm = TRUE))
           
         ) ~ "Gentrifying",
         
         # tracts with above-median percent increases in bachelor degree attainment
            # and EITHER in .75 percentile increase in home values or rents = intensely gentrifying
         
         Percent_bach_PCT >= median(Percent_bach_NET, na.rm = TRUE) & 
           (Med_Value_NET >= quantile(Med_Value_NET,.75, na.rm = TRUE) | 
              Med_Rent_NET >= quantile(Med_Rent_NET,.75, na.rm = TRUE)
           )
          
          ~ "Intensely gentrifying")
  ) %>%
  
  select(GEOID, indicator, Total_Pop_2022, Total_Pop_2017, 
         Med_Inc_2022, Med_Inc_2017,
         Percent_bach_NET, Med_Value_NET, Med_Rent_NET) 

Permit Selection

We can see below how each permit type relates to each gentrification indicator, and we can also calculate the net difference and percent difference between the number of permits in not gentrifying tracts versus intensely gentrifying tracts.

indicator_table <- indicator %>% 
    dplyr::select(-Total_Pop_2022, -Total_Pop_2017, 
         -Med_Inc_2022, -Med_Inc_2017,
         -Percent_bach_NET, -Med_Value_NET, -Med_Rent_NET) %>%
    left_join(tract_permits) %>%
  st_drop_geometry() %>%
  select(-GEOID) %>%
  gather(Variable, value, -indicator) %>%
  group_by(indicator, Variable) %>%
  summarise_if(is.numeric, mean, na.rm = TRUE) %>%
  ungroup() %>%
  spread(key = indicator, value = value) %>%
  mutate_if(is.numeric, round, digits = 3) %>%
  select(Variable, Ineligible, `Not gentrifying`, Gentrifying, `Intensely gentrifying`) 

keep <- indicator_table %>%
  mutate(g_to_non_g_PCT = round((Gentrifying - `Not gentrifying`) / `Not gentrifying`, 4) *100,
         ig_to_non_g_PCT = round((`Intensely gentrifying` - `Not gentrifying`) / `Not gentrifying`, 4) *100) %>%
  filter(Gentrifying >= 1 & `Intensely gentrifying`>=1 & g_to_non_g_PCT > 0 & ig_to_non_g_PCT > 0) %>%
  select(-g_to_non_g_PCT, -ig_to_non_g_PCT)

keep %>%
  datatable()
keep <- unique(keep$Variable) 

Here, we filter only for permits where there is a non-zero percent difference between intensely gentrifying tracts and non-gentrifying tracts, and keep only those permits and attach that to census data, so each tract is assigned an indicator for whether or not it is gentrifying.

tract_permits_final <- res_permits2 %>%
  st_drop_geometry() %>%
  mutate(permit = paste(permitdescription, "-", typeofwork),
         year = year(as.Date(permitissuedate))) %>%
  filter(year >= 2018 & permit %in% keep) %>%
  select(-permitissuedate, -permitdescription, -permittype, -typeofwork) %>%
  gather(Variable, value, -GEOID, -year) %>%
  dplyr::count(Variable, value, GEOID, year) %>%
  group_by(GEOID, year) %>%
  mutate(TOTAL = sum(n)) %>%
  ungroup() %>%
  spread(key = value, value = n) %>%
  select(GEOID, year, TOTAL) %>%
  replace(is.na(.), 0) 

census_permits <- 
  expand.grid(GEOID = unique(tract_permits_final$GEOID),
              year=unique(tract_permits_final$year)) %>%
  left_join(indicator %>% select(GEOID, indicator)) %>%
  left_join(tract_permits_final) %>%
  left_join(census_22_and_comparison %>% st_drop_geometry()) %>%
  mutate(TOTAL = ifelse(is.na(TOTAL), 0, TOTAL)) %>%
  filter(Total_Pop_2022 > 0) %>% 
  select(-geometry) %>%
  left_join(phl_tracts4326) %>%
  st_as_sf()
  
census_permits$indicator <- factor(census_permits$indicator, ordered = TRUE, levels = c("Ineligible", "Not gentrifying", "Gentrifying", "Intensely gentrifying"))

Exploratory Analysis of Filtered Dependent Variable

Now that we have filtered what will ultimately become our dependent variable, we can see that there are many census tracts where there are no permits issued across all years.

ggplot(census_permits) +
  geom_histogram(aes(x = TOTAL), bins = 40, fill = "#ffb600") +
  labs(title = "Permit Distribution", caption = "Figure 4")

There was a huge spike of permits issued in 2020 citywide, likely spurred by the announced rollback of the tax abatement period and the historically low interest rates. As a result, 2021 was a major permit boom year and December 2021 experienced the the highest count of new construction permits issued since January 2016. The boom continued in 2022 and news articles throughout the years commented on the “absurd” number of new consruction permits issued in Philadelphia.

census_permits %>%
  group_by(year) %>%
  summarise(TOTAL = sum(TOTAL)) %>%
  filter(!is.na(year)) %>%
  st_drop_geometry() %>%
  ggplot(aes (x = year, y = TOTAL)) +
  geom_bar(stat = "identity", fill = "#ffb600") +
  plotTheme() +
  labs(title = "Permits by Year", caption = "Figure 5")

National and city-wide policies may be influencing development on a city scale. Gentrification indicators provide a clue as to which tracts are experiencing change, or very rapid change, if at all. But we haven’t yet honed in on neighborhoods to guide our feature engineering. We looked at permits by neighborhood for some clues and could immediately see that there were high permits issued for neighborhoods that have been the subject of locla articles about gentrification, including Fishtown, Point Breeze, and Lower Kensington

res_permits3 <- res_permits %>%
  group_by(permitdescription, typeofwork) %>%
  mutate(n = n()) %>%
  ungroup() %>%
  select(permittype, permitdescription, typeofwork, geometry, permitissuedate) %>%
  st_join(phl.nh) %>%
    mutate(permit = paste(permitdescription, "-", typeofwork),
         year = year(as.Date(permitissuedate))) %>%
  filter(year>=2018 & permit %in% keep)

res_permits3 <- res_permits3 %>%
  st_drop_geometry() %>%
  group_by(mapname, year) %>%
  summarise(n = n()) %>%
  arrange(desc(n))

res_permits3 %>%
  datatable()

When we look at this over time, we can see clearly that the neighborhood patterns remain the same, with the same neighborhoods receiving permits across a 6-year period.

res_permits3 <- res_permits3 %>%
  left_join(phl.nh)

res_permits3 %>%
  ungroup() %>%
  st_as_sf() %>%
  ggplot() +
  geom_sf(aes(fill = cut(n, breaks = 7))) +
  scale_fill_manual(values = palette1) +
  facet_wrap(~year) +
  mapTheme() +
  labs(title = "Permits by Year, Philadelphia", caption = "Figure 6")

Now that we have explored gentrification indicators across Philadelphia neighborhoods and tracts, permits issued over time and space, and some basic demographic characteristics, we developed a composite map where we can see which neighborhoods have gentrification indicators and are seeing a lot of new development for a given year. Click through the years to look at how gentrification indicators and permits change over time.

In 2021, there are area that are gentrifying but not seeing a lot of new development - particularly in the Northeast and Northwest. Predicting development is therefore going to be slightly different than predicting changes in rents, home values, and other demographic characteristics like racial makeup.

bins = c(0,5,30,50,100,200,300,Inf)

permits18 <- colorBin(
  palette = "viridis",
  bins = bins,
  domain = census_permits %>% filter(year == 2018) %>% .$TOTAL)

permits19 <- colorBin(
  palette = "viridis",
  bins = bins,
  domain = census_permits %>% filter(year == 2019) %>% .$TOTAL)

permits20 <- colorBin(
  palette = "viridis",
  bins = bins,
  domain = census_permits %>% filter(year == 2020) %>% .$TOTAL)

permits21 <- colorBin(
  palette = "viridis",
  bins = bins,
  domain = census_permits %>% filter(year == 2021) %>% .$TOTAL)

permits22 <- colorBin(
  palette = "viridis",
  bins = bins,
  domain = census_permits %>% filter(year == 2022) %>% .$TOTAL)

permits23 <- colorBin(
  palette = "viridis",
  bins = bins,
  domain = census_permits %>% filter(year == 2023) %>% .$TOTAL)

indicator_pal <- colorFactor(palette = c("#c191d9", "#6d5480","#ff66f0", "#fccd32"),
                         domain = census_permits$indicator)

permits_map <- leaflet() %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  addPolygons(data = census_permits %>% filter(year == 2023),
              stroke = TRUE,
              color = ~indicator_pal(indicator),
              weight = 2,
              opacity = 1,
              fillOpacity = .5,
              fillColor = ~permits23(TOTAL),
              label = paste("Total Permits:", census_permits %>% filter(year == 2023) %>% .$TOTAL),
              group = "2023") %>%
      addPolygons(data = census_permits %>% filter(year == 2022),
              stroke = TRUE,
              color = ~indicator_pal(indicator),
              weight = 2,
              opacity = 1,
              fillOpacity = .5,
              fillColor = ~permits22(TOTAL),
              label = paste("Total Permits:", census_permits %>% filter(year == 2022) %>% .$TOTAL),
              group = "2022") %>%
  addPolygons(data = census_permits %>% filter(year == 2021),
              stroke = TRUE,
              color = ~indicator_pal(indicator),
              weight = 2,
              opacity = 1,
              fillOpacity = .5,
              fillColor = ~permits21(TOTAL),
              label = paste("Total Permits:", census_permits %>% filter(year == 2021) %>% .$TOTAL),
              group = "2021") %>%
  addPolygons(data = census_permits %>% filter(year == 2020),
              stroke = TRUE,
              color = ~indicator_pal(indicator),
              weight = 2,
              opacity = 1,
              fillOpacity = .5,
              fillColor = ~permits20(TOTAL),
              label = paste("Total Permits:", census_permits %>% filter(year == 2020) %>% .$TOTAL),
              group = "2020") %>%
    addPolygons(data = census_permits %>% filter(year == 2019),
              stroke = TRUE,
              color = ~indicator_pal(indicator),
              weight = 2,
              opacity = 1,
              fillOpacity = .5,
              fillColor = ~permits19(TOTAL),
              label = paste("Total Permits:", census_permits %>% filter(year == 2019) %>% .$TOTAL),
              group = "2019") %>%
  addPolygons(data = census_permits %>% filter(year == 2018),
              stroke = TRUE,
              color = ~indicator_pal(indicator),
              weight = 2,
              opacity = 1,
              fillOpacity = .5,
              fillColor = ~permits18(TOTAL),
              group = "2018",
              label = paste("Total Permits:", census_permits %>% filter(year == 2018) %>% .$TOTAL)) %>%
  addLegend(data = census_permits,
    pal = indicator_pal,
            value = ~indicator,
    title = "Gentrification Indicator",
    position = "bottomleft") %>%
   addLayersControl(
    baseGroups = c("2023", "2022", "2021", "2020", "2019", "2018"),
    options = layersControlOptions(collapsed = FALSE)) 

permits_map %>%
  setView("-75.139446","39.996849",zoom = 11)

When we just look at our gentrification indicator alone (without permits), we can see that not all development is a result of gentrification, but there are still some areas where the two overlap. Although this project aims to provide targeted areas for Housing Trust Funds, we have to recognize that permits alone do not perfectly align with gentrification, but merely reflect new development.

census_permits %>%
  filter(!is.na(year)) %>%
  st_sf() %>%
  ggplot()+
  geom_sf(aes(fill = indicator)) +
  scale_fill_manual(values = palette1) +
  mapTheme() +
  labs(title = "Gentrification Indicators", caption = "Figure 7")

FEATURE ENGINEERING

Spatial and spatial time lags, Census data

Developing spatial and spatial time lags can help us capture the spatial endogeneity of gentrification as discussed here. We created a spatial weights matrix based on contiguity relationships of the census tracts sharing edges and vertices. Specifically, we looked at the nearest neighbor for prices, rents, and demographics of neighboring census tracts as possible key variables to predict for permit development.

We also created a lag for permits issued to last year’s neighbors. Spatial lag and spatial time lag has major predictive potential for our modeling efforts. The average permit count for the nearest census tract can be helpful, but utilizing the nearest neighbor for social demographic data has the potential to be more influence. As high-demand neighborhoods become more price unattainable, residents priced out try to remain close and choose neighborhoods with some adjacent characteristics such as bachelors degerees, income, or white residents, and therefore more likelihood of higher social integration.

nb <- st_contiguity(census_permits$geometry, queen = TRUE)
wt <- st_weights(nb)

census_permits <- census_permits %>%
  mutate(nn_mean_rent_22 = st_lag(Med_Rent_2022,nb,wt, na_ok = TRUE),
      nn_mean_price_22 = st_lag(Med_Value_2022, nb,wt, na_ok = TRUE),
      nn_mean_rent_17 = st_lag(Med_Rent_2017,nb,wt, na_ok = TRUE),
      nn_mean_price_17 = st_lag(Med_Value_2017, nb,wt, na_ok = TRUE),
      nn_bach_22 = st_lag(Percent_bach_2022, nb,wt, na_ok = TRUE),
      nn_bach_17 = st_lag(Percent_bach_2017, nb,wt, na_ok = TRUE),
      nn_income_22 = st_lag(Med_Inc_2022, nb,wt, na_ok = TRUE),
      nn_income_17 = st_lag(Med_Inc_2017, nb,wt, na_ok = TRUE),
      nn_pct_white_22 = st_lag(Percent_White_2022, nb,wt, na_ok = TRUE),
      nn_pct_white_17 = st_lag(Percent_White_2017, nb,wt, na_ok = TRUE)
      )

census_permits <- census_permits %>%
  group_by(GEOID) %>%
  arrange(GEOID, year) %>%
  mutate(last_year_TOTAL = dplyr::lag(TOTAL),
         last_year_TOTAL = ifelse(is.na(last_year_TOTAL), TOTAL, last_year_TOTAL)) %>%
  ungroup() %>%
  mutate(nn_LY_permits = st_lag(last_year_TOTAL, nb, wt))

Local Moran’s I

Moran’s I can help identify spatial autocorrelation, and similarity among clusters of values. Moran’s I values throughout Philadelphia are largely above 1, indicating statistically significant clusters wherein nearest neighbors are having an impact on permit count. Below, we can see that that most of Philadelphia has very low Moran’s I values except for Fishtown and University City, which are outliers. Similarly, there are higher probability values in the areas surrounding Fishtown and University City, particularly in West Philly.

Higher probability for the nearest neighborhoods surrounding the high demand neighborhoods suggest that there is some uncertainty regarding prediction for these tracts. Although this generally indicates that our confidence for predicting these tracts is lower, the clustering of these probability values suggests that the nearest neighbor and spatial lag is indicative of some factor, even if we are not identifying it with exactness.

nb <- poly2nb(census_permits$geometry, queen=TRUE)
wt <- nb2listw(nb, style="W", zero.policy=TRUE)

local_morans <- localmoran(census_permits$TOTAL, wt, zero.policy=TRUE) %>%
  as.data.frame() %>%
  rename(c("Local_Morans_I" = "Ii", "P_Value" = "Pr(z != E(Ii))"))

census_permits <- 
  cbind(census_permits, local_morans %>% select(Local_Morans_I, P_Value)) %>% 
  st_sf() %>%
  mutate(Significant_Hotspots = ifelse(P_Value <= 0.001, 1, 0)) 

#Plot Morans I

localMorans <- 
  cbind(local_morans, census_permits) %>% 
  st_sf() %>%
  dplyr::select(TOTAL,
                Local_Morans_I,
                P_Value) %>%
  mutate(Significant_Hotspots = ifelse(P_Value <= 0.001, 1, 0)) %>%
  gather(Variable, Value, -geometry)

## This is just for plotting
vars <- unique(localMorans$Variable)
varList <- list()

for(i in vars){
  varList[[i]] <- 
    ggplot() +
      geom_sf(data = filter(localMorans, Variable == i), 
              aes(fill = Value), colour=NA) +
      scale_fill_viridis_b(name="") +
      labs(title=i) +
      mapTheme(title_size = 14) + theme(legend.position="bottom")
}


do.call(grid.arrange,c(varList, ncol = 4, top = "Local Morans I statistics, Total Permits"))

Additional Feature Engineering

Coffee shops

Finally, we generated a few amenity features such as stores or infrastructure that may make a neighborhood more attractive. These include cafes from 2018 to 2023 since the presence of new cafes may be correlated with gentrification or new development.

#coffee shops by year, then join to census_permits.

 # cafes18 <- getbb('Philadelphia, PA') %>%
 #   opq(datetime = '2018-01-01T00:00:01Z') %>%
 #   add_osm_feature(key = "amenity", value = "cafe") %>%
 #   osmdata_sf()
 # 
 # cafes18.sf <- st_as_sf(cafes18$osm_points)
 # 
 # cafes18.sf <- cafes18.sf %>%
 #   st_transform(crs = st_crs(census_permits)) %>%
 #   st_join(census_permits, join = st_within)
 # 
 # cafes18.sf <- cafes18.sf %>%
 #   filter(!is.na(GEOID)) %>%
 #   group_by(GEOID) %>%
 #   summarise(cafe_count = n()) %>%
 #   mutate(cafeyear = 2018)
 # 
 # cafes19 <- getbb('Philadelphia, PA') %>%
 #   opq(datetime = '2019-01-01T00:00:01Z') %>%
 #   add_osm_feature(key = "amenity", value = "cafe") %>%
 #   osmdata_sf()
 # 
 # cafes19.sf <- st_as_sf(cafes19$osm_points) %>%
 #   st_transform(crs = st_crs(census_permits)) %>%
 #      st_join(census_permits, join = st_within) %>%
 #   filter(!is.na(GEOID)) %>%
 #   group_by(GEOID) %>%
 #   summarise(cafe_count = n()) %>%
 #   mutate(cafeyear = 2019)
 # 
 # cafes20 <- getbb('Philadelphia, PA') %>%
 #   opq(datetime = '2020-01-01T00:00:01Z') %>%
 #   add_osm_feature(key = "amenity", value = "cafe") %>%
 #   osmdata_sf()
 # 
 # cafes20.sf <- st_as_sf(cafes20$osm_points) %>%
 #   st_transform(crs = st_crs(census_permits)) %>%
 #   st_join(census_permits, join = st_within) %>%
 #   filter(!is.na(GEOID)) %>%
 #   group_by(GEOID) %>%
 #   summarise(cafe_count = n()) %>%
 #   mutate(cafeyear = 2020)
 # 
 # cafes21 <- getbb('Philadelphia, PA') %>%
 #   opq(datetime = '2021-01-01T00:00:01Z') %>%
 #   add_osm_feature(key = "amenity", value = "cafe") %>%
 #   osmdata_sf()
 # 
 #  cafes21.sf <- st_as_sf(cafes21$osm_points) %>%
 #   st_transform(crs = st_crs(census_permits)) %>%
 #   st_join(census_permits, join = st_within) %>%
 #   filter(!is.na(GEOID)) %>%
 #   group_by(GEOID) %>%
 #   summarise(cafe_count = n()) %>%
 #   mutate(cafeyear = 2021)
 # 
 # cafes22 <- getbb('Philadelphia, PA') %>%
 #   opq(datetime = '2022-01-01T00:00:01Z') %>%
 #   add_osm_feature(key = "amenity", value = "cafe") %>%
 #   osmdata_sf()
 # 
 # cafes22.sf <- st_as_sf(cafes22$osm_points) %>%
 #   st_transform(crs = st_crs(census_permits)) %>%
 #   st_join(census_permits, join = st_within) %>%
 #   filter(!is.na(GEOID)) %>%
 #   group_by(GEOID) %>%
 #   summarise(cafe_count = n()) %>%
 #   mutate(cafeyear = 2022)
 # 
 # cafes23 <- getbb('Philadelphia, PA') %>%
 #   opq(datetime = '2023-01-01T00:00:01Z') %>%
 #   add_osm_feature(key = "amenity", value = "cafe") %>%
 #   osmdata_sf()
 # 
 # cafes23.sf <- st_as_sf(cafes23$osm_points) %>%
 #   st_transform(crs = st_crs(census_permits)) %>%
 #   st_join(census_permits, join = st_within) %>%
 #   filter(!is.na(GEOID)) %>%
 #   group_by(GEOID) %>%
 #   summarise(cafe_count = n()) %>%
 #   mutate(cafeyear = 2023)
 # 
 #  cafes_list <- list(cafes18.sf, cafes19.sf, cafes20.sf, cafes21.sf, cafes22.sf,
 #                     cafes23.sf)
 # 
 # all_cafes <- reduce(cafes_list, bind_rows) %>%
 #   rename("year" = "cafeyear") %>%
 #  st_drop_geometry()
 # 
 # 
 # write.csv(all_cafes, "Data/all_cafes.csv")
 # 

# rm(cafe18, cafes18.sf, cafes19, cafes19.sf, cafes20, cafes20.sf, cafes21, cafes21.sf, cafes22, cafes22.sf, cafes23, cafes23.sf)

 all_cafes <- read.csv("Data/all_cafes.csv") %>%
   select(-X)

all_cafes$GEOID <-  as.character(all_cafes$GEOID)


  census_permits <- census_permits %>%
    left_join(all_cafes, by = c("GEOID", "year")) %>%
     mutate(cafe_count = ifelse(is.na(cafe_count), 0, cafe_count))

Distance to transit stations

We also looked at proximity to fixed public transportation within the city of Philadelphia, including regional rail, the Broad Street Line, Market Frankford Line, and trolley lines.

regionalrail <- st_read("Data/Regional_Rail_Stations.geojson") 

trolleys <- st_read("Data/Trolley_Stations.geojson") %>%
  rename(Line_Name = LineAbbr,
         Latitude = Lat,
         Longitude = Lon,
         Station_Na = StopName) %>%
  select(FID, Line_Name, Station_Na, Latitude, Longitude, geometry)
  
metro <- st_read("Data/Highspeed_Stations.geojson") %>%
  rename(Line_Name = Route, 
         Station_Na = Station)

transit <- rbind(regionalrail, trolleys, metro) %>%
    st_transform(crs = st_crs(phl_tracts22))


phl_transit <- st_join(transit, phl_tracts22, join = st_within) %>%
  filter(!is.na(GEOID)) %>%
  select(Line_Name, Station_Na, Latitude, Longitude, GEOID, geometry) %>%
  st_transform(crs = st_crs(census_permits))

census_permits <- census_permits %>%
  mutate(nearest_transit = nn_function(st_coordinates(st_centroid(census_permits)), st_coordinates(phl_transit), k=1)*69.2)

Number of jobs within tract

Finally, we looked at job counts per tract. Although we initially were going to look at places such as major universities or distance to center city, ultimately, we recognized that these locations are substitutes for job density. We looked at jobs over time, but overall the proportion remains pretty similar across the years for which we have data. Therefore, we interpolate the geographies to 2020 tracts and then look at spatial lag – jobs in surrounding census tracts. This feature could use some further development, because our visual assessment is that people do not want to live in the areas with the highest job density, but a within a certain distance of those job centers.

#use Lehdr package to bring in jobs and to analyze where people are going by work and home census tract. 
#derived from: https://github.com/jamgreen/lehdr 

#pa jobs from LEHDR
#match with lodes data from previous year, grab data for 2019 - 2022
#match the individual permit sheet with the previous years job data
# 
# pa_jobs19 <- grab_lodes(state = "pa",
#                        year = 2019,
#                        version = "LODES8",
#                        lodes_type = "od",
#                        job_type = "JT01",
#                        segment = "S000",
#                        state_part = "main",
#                        agg_geo = "tract") %>%
#   select(-h_tract)
# 
# pa_jobs20 <- grab_lodes(state = "pa",
#                        year = 2020,
#                        version = "LODES8",
#                        lodes_type = "od",
#                        job_type = "JT01",
#                        segment = "S000",
#                        state_part = "main",
#                        agg_geo = "tract")%>%
#   select(-h_tract)
# 
# pa_jobs21 <- grab_lodes(state = "pa",
#                        year = 2021,
#                        version = "LODES8",
#                        lodes_type = "od",
#                        job_type = "JT01",
#                        segment = "S000",
#                        state_part = "main",
#                        agg_geo = "tract")%>%
#   select(-h_tract)
# 
# #filter for only jobs that start or end in Philadelphia tracts
# 
# phl_jobs19 <- pa_jobs19 %>%
#   left_join(phl_tracts22, by = c("w_tract" = "GEOID")) %>%
#     filter(!st_is_empty(geometry)) %>%
#   st_as_sf() %>%
#   rename(GEOID = w_tract) %>%
#   group_by(GEOID) %>%
#   summarise(job_count = n())
# 
# phl_jobs19 <- interpolate_pw(
#   phl_jobs19,
#   phl_tracts22,
#   to_id = "GEOID",
#   extensive = TRUE, 
#   weights = phl_tracts22,
#   weight_column = "Total_Pop",
#   crs = 4269)
# 
# phl_jobs20 <- pa_jobs20 %>%
#   left_join(phl_tracts22, by = c("w_tract" = "GEOID")) %>%
#     filter(!st_is_empty(geometry)) %>%
#   st_as_sf() %>%
#   rename(GEOID = w_tract) %>%
#   group_by(GEOID) %>%
#   summarise(job_count = n())
# 
# phl_jobs21 <- pa_jobs21 %>%
#   left_join(phl_tracts22, by = c("w_tract" = "GEOID")) %>%
#     filter(!st_is_empty(geometry)) %>%
#   st_as_sf() %>%
#   rename(GEOID = w_tract) %>%
#   group_by(GEOID) %>%
#   summarise(job_count = n())
# 
# phl_jobs_combined <- rbind(cbind(phl_jobs19, year = 2019),
#                            cbind(phl_jobs20, year = 2020),
#                            cbind(phl_jobs21, year = 2021))
# 
# ggplot(phl_jobs_combined) +
#   geom_sf(aes(fill = job_count)) +
#   labs(title = "Job Distribution in Philadelphia", color = "Job Count") +
#   mapTheme() +
#   facet_wrap(~year, ncol = 3)
# 
# phl_jobs_combined$year <- as.Numeric(phl_jobs_combined$year)

phl_jobs_combined <- read.csv("Data/phl_jobs.csv") %>%
  select(-X) %>%
  mutate(GEOID = as.character(GEOID)) %>%
  rename("year_jobs" = "year")

census_permits <- census_permits %>%
  mutate(year_jobs = case_when(year == 2018 ~ 2019,
                               year == 2022 ~ 2021,
                             year == 2023 ~ 2021,
                             year %in% c(2019,2020,2021) ~ year)) %>%
  left_join(phl_jobs_combined, 
            by = c("GEOID","year_jobs")) %>%
  select(-year_jobs)

phl_jobs_combined <- phl_jobs_combined %>%
  left_join(phl_tracts22, by = c("GEOID" = "GEOID")) %>%
  select(job_count, GEOID, geometry) %>%
  st_as_sf()

nb <- st_contiguity(census_permits$geometry, queen = TRUE)
wt <- st_weights(nb)

census_permits <- census_permits %>%
   mutate(jobs.nn= st_lag(job_count,nb,wt, na_ok = TRUE))

Neighborhoods

census_permits <- st_join(st_centroid(census_permits), phl.nh, join = st_within) %>%
  st_drop_geometry() %>%
  left_join(phl_tracts4326) %>%
  st_as_sf()

EXPLORATORY ANALYSIS

Here, we look at our dependent variable, total permits, as a function of our numeric variables. Last year’s permits and the Local Moran’s I are most strongly correlated.

Some of these graphs show that permits spike for values in the middle of the distribution. As an experiment, we could make a binary variable for relationships where there’s a spike in the middle - between the 45th and 55th percentile or not.

#filter the outliers 
census_permits_test <- census_permits %>%
  filter(TOTAL < 600) %>%
  dplyr::select(-indicator, -mapname, -name, -Significant_Hotspots) %>%
  gather(variable, value, -geometry, -year, -GEOID, -TOTAL)

ggplot(census_permits_test, aes(value, TOTAL)) +
  geom_point() +
  stat_smooth(aes(value, TOTAL), 
              method = "lm", se = FALSE, size = 1, colour="#ffb600") +
  facet_wrap(~variable, scales= "free", ncol=4) +
  labs(title = "Exploratory Analysis", caption = "Figure 8")

We can test this theory out on the variables that appear to have the largest spikes in the middle. It turns out this is only true for three variables – the middle percentile (between 45th and 55th percentile) is related to the number of permits for the change in percentage of homes with a second mortgage between 2017 and 2022 5-Year ACS surveys, for 2017 adjusted mean rent, and for percent owner in 2022.

census_permits_test <- census_permits %>%
  mutate(
    middle_bach_17 = ifelse(Bach_2017 > quantile(Bach_2017, .45, na.rm = TRUE) & Bach_2017 <= quantile(Bach_2017, .55, na.rm = TRUE), TRUE, FALSE),
    
    #maybe this one
         middle_rent_17 = ifelse(Med_Rent_2017 > quantile(Med_Rent_2017, .45, na.rm = TRUE) & Med_Rent_2017 <= quantile(Med_Rent_2017, .55, na.rm = TRUE), TRUE, FALSE),
    
         middle_price_NET = ifelse(Med_Value_NET > quantile(Med_Value_NET, .45, na.rm = TRUE) & Med_Value_NET <= quantile(Med_Value_NET, .55, na.rm = TRUE), TRUE, FALSE),
    
         middle_nn_bach_22 = ifelse(nn_bach_22 > quantile(nn_bach_22, .45, na.rm = TRUE) & nn_bach_22 <= quantile(nn_bach_22, .55, na.rm = TRUE), TRUE, FALSE),
    
         middle_nn_income_22 = ifelse(nn_income_22 > quantile(nn_income_22, .45, na.rm = TRUE) & nn_income_22 <= quantile(nn_income_22, .55, na.rm = TRUE), TRUE, FALSE),
    
         middle_nn_mean_price_22 = ifelse(nn_mean_price_22 > quantile(nn_mean_price_22, .45, na.rm = TRUE) & nn_mean_price_22 <= quantile(nn_mean_price_22, .55, na.rm = TRUE), TRUE, FALSE),
    
         middle_nn_mean_rent_22 = ifelse(nn_mean_rent_22 > quantile(nn_mean_rent_22, .45, na.rm = TRUE) & nn_mean_rent_22 <= quantile(nn_mean_rent_22, .55, na.rm = TRUE), TRUE, FALSE),
    
         middle_Owners_PCT = ifelse(Owners_PCT > quantile(Owners_PCT, .45, na.rm = TRUE) & Owners_PCT <= quantile(Owners_PCT, .55, na.rm = TRUE), TRUE, FALSE),
    
    #this one
         middle_Percent_2mort_NET = ifelse(Percent_2mort_NET > quantile(Percent_2mort_NET, .45, na.rm = TRUE) & Percent_2mort_NET <= quantile(Percent_2mort_NET, .55, na.rm = TRUE), TRUE, FALSE),
    
         middle_Percent_bach_2022 = ifelse(Percent_bach_2022 > quantile(Percent_bach_2022, .45, na.rm = TRUE) & Percent_bach_2022 <= quantile(Percent_bach_2022, .55, na.rm = TRUE), TRUE, FALSE),
    
         middle_Percent_bach_PCT = ifelse(Percent_bach_PCT > quantile(Percent_bach_PCT, .45, na.rm = TRUE) & Percent_bach_PCT <= quantile(Percent_bach_PCT, .55, na.rm = TRUE), TRUE, FALSE),
    
    #this one
         middle_Percent_Owner_2022 = ifelse(Percent_Owner_2022 > quantile(Percent_Owner_2022, .45, na.rm = TRUE) & Percent_Owner_2022 <= quantile(Percent_Owner_2022, .55, na.rm = TRUE), TRUE, FALSE),
    
         middle_Rent_Income_Ratio_2017 = ifelse(Rent_Income_Ratio_2017 > quantile(Rent_Income_Ratio_2017, .45, na.rm = TRUE) & Rent_Income_Ratio_2017 <= quantile(Rent_Income_Ratio_2017, .55, na.rm = TRUE), TRUE, FALSE),
    
    #Also testing this new binary variable!
    transit_under_1.5 = ifelse(nearest_transit <= 1.5, TRUE, FALSE)
                                 ) %>%
  
  dplyr::select(TOTAL, starts_with('middle'), transit_under_1.5) %>%
  st_drop_geometry() %>%
  gather(variable, value, -TOTAL)

census_permits_test %>%
  filter(TOTAL < 600) %>%
  group_by(variable, value) %>%
  summarise(mean_permits = mean(TOTAL)) %>%
  ungroup() %>%
    ggplot(aes(value,mean_permits)) + 
      geom_bar(position = "dodge", stat= "identity", fill = "#ffb600") + 
      facet_wrap(~variable, scales = "free") +
      labs(x="Total Permits", y="Value", 
           title = "Middle percentile (45-55th) associations with number of permits", caption = "Figure 9") +
      theme(legend.position = "none") 

# Mutating only for the variables variables that mattered

census_permits <- census_permits %>%
  mutate(middle_Percent_Owner_2022 = ifelse(Percent_Owner_2022 > quantile(Percent_Owner_2022, .45, na.rm = TRUE) & Percent_Owner_2022 <= quantile(Percent_Owner_2022, .55, na.rm = TRUE), TRUE, FALSE),
         
         middle_Percent_2mort_NET = ifelse(Percent_2mort_NET > quantile(Percent_2mort_NET, .45, na.rm = TRUE) & Percent_2mort_NET <= quantile(Percent_2mort_NET, .55, na.rm = TRUE), TRUE, FALSE),
         
          middle_rent_17 = ifelse(Med_Rent_2017 > quantile(Med_Rent_2017, .45, na.rm = TRUE) & Med_Rent_2017 <= quantile(Med_Rent_2017, .55, na.rm = TRUE), TRUE, FALSE),
         
         transit_under_1.5 = ifelse(nearest_transit <= 1.5, TRUE, FALSE))
#Mapping cafes and vacancy:

# ggplot(census_permits) +
#   geom_sf(aes(fill = cafe_count)) +
#   labs(title = "Number of Cafes per Tract") +
#   facet_wrap(~year) +
#   mapTheme()
# 
# ggplot(census_permits %>% filter(year == 2023)) +
#   geom_sf(aes(fill = Percent_Vacant_2016)) +
#   labs(title = "Vacancy Rate 2016") +
#   mapTheme()

Correlation Matrix

We compared variables against each other in a correlation test. Many of the variables correlated with each other more than with the count of total permits. The most correlated variables with permit count are vacancy with a positive correlation, wherein more vacancy means higher permit issuance. After some additional analysis and feature engineering however, we realize that close proximity to transit (within 1.5 mile) is more significant than the nearest neighbor transit. Variables are of course correlated with each other, but overall census variables are not as correlated with permit count as expected.

numericVars <- census_permits %>%
  st_drop_geometry() %>%
  select(TOTAL, cafe_count, nearest_transit, job_count, Med_Inc_2022, Rent_Income_Ratio_NET,
         White_Pop_2022, Med_Value_NET, Vacants_2022, Vacants_2017,
         Vacants_PCT, Vacants_NET) 

price.corr <- corr.test(numericVars)

if (sum(is.na(numericVars)) > 0) {
  # Handle missing values (e.g., impute or remove)
  numericVars <- na.omit(numericVars)  # Remove rows with missing values
}

correlation_matrix <- round(cor(numericVars), 1)
p_values <- cor_pmat(numericVars)

ggcorrplot(
  correlation_matrix,
  p.mat = p_values,
  colors = c( "#f20089", "white", "#2d00f7"),
  type = "lower",
  insig = "blank"
  
) +
labs(title = "Degrees of Correlation to Permits Issued", caption = "Figure 10") +
  annotate(
  geom = "rect",
  xmin = .5, xmax = 11.5, ymin = .5, ymax = 1.5,
  fill = "transparent", color = "#ffb600", alpha = 0.5
  

)

MODELING

For the baseline, we included census variables including median HH income (CPI inflation adjusted), Median home value, Median rent, Median year structure built, tenure, number of housing units, number of vacancies, count of second mortgages, percent with a Bachelor’s degree, and percent white population.

After doing our exploratory analysis, we narrowed down the variables for our model. For the spatial and temporal lag model, we used these variables, grouped into the following categories:

TIME/SPACE
- name (neighborhood)
- year

GENTRIFICATION INDICATORS
- indicator (incorporates bachelors degrees, income price changes, rent changes, and population)

DEMOGRAPHIC (the strongest)
- Bach_NET
- middle_Percent_Owner_2022
- middle_Percent_2mort_NET

(other demographic variables) - Med_Value_NET
- Percent_2mort_2022
- Percent_bach_NET
- Rent_Income_Ratio_2022
- White_Pop_NET

AMENITIES
- cafe_count
- job_count
- transit_under_1.5

TIME LAG
- last_year_TOTAL
- Med_Value_2017
- Total_Units_2017
- Vacants_2017
- middle_rent_17

SPATIAL LAG
- Local Moran’s I
- nn_LY_permits
- nn_bach_22
- nn_income_17

Several iterations of models were developed over the course of this project, each attempting to isolate and coalesce variables in different combinations to create as accurate a prediction as possible.

We used three cross-validation methods: random 10-fold cross-validation, spatial cross-validation, and temporal cross-validation. By cross validating, we can detect generalizability & accuracy for the model’s predictions across time and space in Philadelphia. Cross validation will also help determine whether our model is overfitting. With overtfitting, the model is very accurate but not generalizable to new datasets as new data becomes available and input into the model.

set.seed(152)

census_permits_dat <- census_permits %>%
  filter(year %in% c(2020, 2021, 2022)) %>%
  mutate(cvID = sample(round(nrow(.) / 115.5), 
                       size=nrow(.), replace = TRUE)) %>%
  filter(name != "BYBERRY" & name != "WISSAHICKON_PARK" & name != "GLENWOOD" & name != "WISTER"
         & name != "MCGUIRE")
# BASELINE

reg.baseline.vars <- c("year","indicator","name","Percent_White_2022","Percent_Owner_2022", "Percent_Vacant_2022", "Med_Inc_2022","Med_Value_2022","Med_Rent_2022", "Med_Structure_2022",
                                    "Tot_Units_2022", "Vacants_2022", "Sec_Mort_2022", "Percent_bach_2022",
                                    "Rent_Income_Ratio_2022",
                       "Bach_NET", "middle_Percent_Owner_2022","middle_Percent_2mort_NET",
                       "Med_Value_NET","Percent_bach_NET",  "White_Pop_NET",
                      "cafe_count", "job_count", "transit_under_1.5")

reg.baseline.vars.space <- reg.baseline.vars[reg.baseline.vars != "name"]
reg.baseline.vars.time <- reg.baseline.vars[reg.baseline.vars != "year"]

# SPACE AND TIME - zero inflated

reg.st.vars <- c( "year", "indicator", "name","Bach_NET","middle_Percent_Owner_2022","middle_Percent_2mort_NET",
                                "Med_Value_NET", "Percent_2mort_2022", "Percent_bach_NET",
                                    "Rent_Income_Ratio_2022", "White_Pop_NET", "cafe_count",
                                    "transit_under_1.5",  "last_year_TOTAL",
                                    "Med_Value_2017", "Tot_Units_2017", "Vacants_2017",
                                    "middle_rent_17",  "Local_Morans_I",  "nn_LY_permits",
                                    "nn_bach_22", "nn_income_17", "jobs.nn")

reg.st.space <-reg.st.vars[reg.st.vars != "name"] 
reg.st.time <- reg.st.vars[reg.st.vars != "year"]

# Baseline
reg.baseline.random <- crossValidate(
  dataset = census_permits_dat,
  id = "cvID",                           
  dependentVariable = "TOTAL",
  indVariables = reg.baseline.vars) %>%
    dplyr::select(cvID = cvID, TOTAL, Prediction, name)

reg.baseline.cv.time <- crossValidate(
  dataset = census_permits_dat,
  id = "year",                           
  dependentVariable = "TOTAL",
  indVariables = reg.baseline.vars.time) %>%
    dplyr::select(cvID = year, TOTAL, Prediction, name)

reg.baseline.cv.space <- crossValidate(
  dataset = census_permits_dat,
  id = "name",                           
  dependentVariable = "TOTAL",
  indVariables = reg.baseline.vars.space) %>%
    dplyr::select(cvID = name, TOTAL, Prediction) %>%
  mutate(name = cvID) %>%
  dplyr::select(cvID, TOTAL, Prediction, name, geometry)

# space/time lag

reg.st.cv.random <- crossValidate(
  dataset = census_permits_dat,
  id = "cvID",                           
  dependentVariable = "TOTAL",
  indVariables = reg.st.vars) %>%
    dplyr::select(cvID = cvID, TOTAL, Prediction, name)

reg.st.cv.time <- crossValidate(
  dataset = census_permits_dat,
  id = "year",                           
  dependentVariable = "TOTAL",
  indVariables = reg.st.time) %>%
    dplyr::select(cvID = year, TOTAL, Prediction, name)

reg.st.cv.space <- crossValidate(
  dataset = census_permits_dat,
  id = "name",                           
  dependentVariable = "TOTAL",
  indVariables = reg.st.space) %>%
    dplyr::select(cvID = name, TOTAL, Prediction) %>%
  mutate(name = cvID) %>%
  dplyr::select(cvID, TOTAL, Prediction, name, geometry)

reg.summary <- 
  rbind(
    mutate(reg.baseline.random,           Error = Prediction - TOTAL,
                             Regression = "Random k-fold CV: Baseline Variables"),
    
    mutate(reg.st.cv.random,           Error = Prediction - TOTAL,
                             Regression = "Random k-fold CV: Spatial and Temporal Lag"),
    
    mutate(reg.baseline.cv.space,    Error = Prediction - TOTAL,
                             Regression = "Spatial LOGO-CV: Baseline Variables"),
    
    mutate(reg.st.cv.space,    Error = Prediction - TOTAL,
                             Regression = "Spatial LOGO-CV: Spatial and Temporal Lag"),
    
    mutate(reg.baseline.cv.time,    Error = Prediction - TOTAL,
                             Regression = "Temporal LOGO-CV: Baseline Variables"),
    
    mutate(reg.st.cv.time,    Error = Prediction - TOTAL,
                             Regression = "Temporal LOGO-CV: Spatial and Temporal Lag")
    
    ) %>%
    st_sf() 

Explore Errors

Mean absolute error is quite low and similar across all cross validation methods with only a few outliers. These outliers have significant count differences, but the rest of the neighborhoods are overal the same. The spatial and temporal lag model when cross validated at first appears to be the strongest model with the lowest error, which suggests that time and space lag factors are the most influential predictors.

error_by_reg <- 
  reg.summary %>%
    group_by(Regression) %>% 
    summarize(Mean_Error = mean(Error, na.rm = T),
              MAE = mean(abs(Error), na.rm = T),
              SD_MAE = sd(abs(Error), na.rm = T)) 

error_by_reg_nh <- 
  reg.summary %>%
    group_by(Regression, name) %>% 
    summarize(Mean_Error = mean(Prediction - TOTAL, na.rm = T),
              MAE = mean(abs(Prediction - TOTAL), na.rm = T),
              SD_MAE = sd(abs(Prediction - TOTAL), na.rm = T)) %>%
  ungroup()

## plot histogram of errors
error_by_reg_nh %>%
  ggplot(aes(MAE)) + 
    geom_histogram(bins = 30, colour="black", fill = "#f20089") +
  facet_wrap(~Regression, ncol = 2) +
    labs(title="Distribution of MAE by Regression and Neighborhood",
         x="Mean Absolute Error", y="Count")

Spatial Distribution of Errors

When we examine the temporal and spatial lag in further detail, it becomes apparent that the neighborhoods with highest outlier in predicted count are near the Fishtown and Southwest Philly neighborhoods. Other than these, error is quite small throughout the rest of Philadelphia.

reg.summary %>%
  filter(Regression == "Temporal LOGO-CV: Spatial and Temporal Lag") %>%
  group_by(name) %>%
  st_drop_geometry() %>%
  summarize(residuals = mean(Error, na.rm = T)) %>%
  ungroup() %>% 
  left_join(phl.nh, by = c("name" = "name")) %>%
    st_sf() %>%
    ggplot() + 
      geom_sf(aes(fill = residuals)) +
      scale_fill_gradient2(low = "#8900f2" , mid =  "#ffb600", high = "#e500a4", name = "Error") +
      labs(title = "Spatial and Temporal Lag: \nMean Absolute Error by Neighborhood", caption = "Figure 10") +
      mapTheme()

Finally, when plotting predicted permits compared with observed permits, we can see that the observed and predicted permits are fairly close but not so much that the model is overfitting. It is therefore generalizable and overall accurate.

reg.summary %>%
  filter(Regression == "Temporal LOGO-CV: Spatial and Temporal Lag") %>%  
  ggplot(aes(TOTAL, Prediction)) +
  geom_point() +
  stat_smooth(aes(TOTAL, TOTAL), 
             method = "lm", se = FALSE, size = 1, colour="#ffb600") + 
  stat_smooth(aes(Prediction, TOTAL), 
              method = "lm", se = FALSE, size = 1, colour="#f20089") +
  labs(title="Predicted permits as a function of observed permits",
       subtitle="Orange line represents a perfect prediction; Pink line represents prediction", caption = "Figure 21") +
  xlab("Actual Permits") +
  ylab("Predicted Permits") +
  plotTheme()

Our spatial regression model cross validates on a neighborhood level across time, while our temporal regression cross validates year by year. Regression error for the temporal logo cross validation with spatial and temporal lag have the lowest mean absolute error as well as standard deviation, which remains consistent with our other findings of that model. It is clear that this model is generalizable, accurate, and overall a good fit across measurements. Because cross validating on a temporal scale appears to have the lowest mean MAE and Standard Deviation, we can be fairly certain that time is key to predicting permit issuance. This is also important since it means that additional years of data will ideally be able to predict permit issuance with future years of datasets.

st_drop_geometry(error_by_reg_nh) %>%
  group_by(Regression) %>% 
    summarize(Mean_MAE = round(mean(MAE), 2),
              SD_MAE = round(sd(MAE), 2)) %>%
  kable() %>%
    kable_styling("striped", full_width = F) %>%
    row_spec(3, color = "black", background = "#8900f2") %>%
    row_spec(4, color = "black", background = "#8900f2") %>%
    row_spec(5, color = "black", background = "#ffb600") %>%
    row_spec(6, color = "black", background = "#ffb600")
Regression Mean_MAE SD_MAE
Random k-fold CV: Baseline Variables 18.68 26.03
Random k-fold CV: Spatial and Temporal Lag 15.66 20.21
Spatial LOGO-CV: Baseline Variables 34.18 39.30
Spatial LOGO-CV: Spatial and Temporal Lag 25.90 45.36
Temporal LOGO-CV: Baseline Variables 17.26 22.59
Temporal LOGO-CV: Spatial and Temporal Lag 14.71 18.95

CONCLUSION

Discussion

Overall, this model is effective and when coupled with indicators of gentrification, can help predict which neighborhoods are most likely areas with high amounts of development and experiencing intense neighborhood change. To prevent displacement for long-term and socioeconomically disadvantaged residents, community land trusts and housing trust funds can fund long-term solutions. Since it is time-consuming to develop, fund, and implement these types of solutions, anticipating a few years out can prevent displacement and create long-term affordable housing. Targeting areas that are experiencing early indicators of gentrification but not high amounts of permit development is a critical strategy.

Use Case Application

Identifying aras for community land trust investment can help as a long term strategy for anti-displacement, like zoning. Community land trusts will be influential in preventing Philadelphia from pushing out its long-term residents. Identifying areas for community land trust investment can help as a long term strategy to fight displacement. We hope that this can help secure affordable housing and predict the best areas to secure it for the future.

For a brief presentation of this markdown, please visit our video here

LS0tCnRpdGxlOiAiVGFyZ2V0aW5nIENvbW11bml0eSBMYW5kIFRydXN0IEludGVydmVudGlvbnMgdG8gRmlnaHQgRGlzcGxhY2VtZW50IgphdXRob3I6ICJMaXp6aWUgU2hhY2tuZXkgYW5kIEFsZXhhIFJpbmdlciIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogY29zbW8KICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIGNvZGVfZm9sZGluZzogImhpZGUiCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCmVkaXRvcl9vcHRpb25zOgogIG1hcmtkb3duOgogICAgd3JhcDogc2VudGVuY2UKICAgIAotLS0KCiMjICoqSU5UUk9EVUNUSU9OKioKCldoZXJlIGlzIGRldmVsb3BtZW50IGdvaW5nIHRvIGhhcHBlbj8gRGV2ZWxvcG1lbnQgaXMgY3JpdGljYWwgdG8gYWNjb21tb2RhdGUgZ3Jvd3RoIGFuZCBjaGFuZ2UsIHRvIHByb3ZpZGUgaG91c2luZywgaW5mcmFzdHJ1Y3R1cmUsIGFtZW5pdGllcywgZW1wbG95bWVudCwgYW5kIGNvbW11bml0eSBzcGFjZXMgZm9yIGVjb25vbWljIHByb3NwZXJpdHkgYW5kIGFmZm9yZGFibGUgc2hlbHRlci4gQnV0IGRldmVsb3BtZW50IGhhcyBpdHMgZG93bnNpZGVzLCB0b28uIFdoZW4gaW52ZXN0bWVudHMgaW4gbmVpZ2hib3Job29kcyBkcml2ZSB1cCBwcmljZXMgYW5kIHRoZSBjb3N0IG9mIGxpdmluZywgaXQgY2FuIOKAkyBlaXRoZXIgZGlyZWN0bHkgb3IgaW5kaXJlY3RseSDigJMgZm9yY2Ugb3V0IGxvbmctdGVybSByZXNpZGVudHMgd2hvIGFyZSBtb3JlIHZ1bG5lcmFibGUgdG8gY29zdCBjaGFuZ2VzLiBVbmRlcnN0YW5kaW5nIGhvdyBlY29ub21pYyBkZXZlbG9wbWVudCBjYW4gZXF1aXRhYmx5IGJlbmVmaXQgcmVzaWRlbnRzIHJhdGhlciB0aGFuIGp1c3QgcHVzaCB0aGVtIHRvIHRoZSB1cmJhbiBwZXJpcGhlcnkgY2FuIGhlbHAgZW5zdXJlIHRoYXQgdXJiYW4gcG9saWN5IGJlbmVmaXRzIGNpdHnigJlzIG1vc3QgdnVsbmVyYWJsZSByZXNpZGVudHMuIFVuZGVyc3RhbmRpbmcgcGF0dGVybnMgb2YgYW5kIHByZWRpY3RpbmcgZGV2ZWxvcG1lbnQgaXMga2V5IHRvIHRoaXMsIGFuZCBpdCdzIGNoYWxsZW5naW5nLiAgCgpBIGtleSB0byB1bmRlcnN0YW5kaW5nIHBhdHRlcm5zIG9mIGRldmVsb3BtZW50IGlzIHVuZGVyc3RhbmRpbmcgW2VuZG9nZW5vdXMgZ2VudHJpZmljYXRpb25dKCBodHRwczovL3VyYmFuc3BhdGlhbGFuYWx5c2lzLmNvbS9wb3J0Zm9saW8vcHJlZGljdGluZy1nZW50cmlmaWNhdGlvbi11c2luZy1sb25naXR1ZGluYWwtY2Vuc3VzLWRhdGEvKS4gRW5kb2dlbm91cyBnZW50cmlmaWNhdGlvbiBkZXNjcmliZXMgdGhlIGluLW1pZ3JhdGlvbiBvZiB3ZWFsdGhpZXIgcmVzaWRlbnRzIHRvIGFkamFjZW50LCBwb29yZXIgbmVpZ2hib3Job29kcywgY2F1c2luZyBwcmljZXMgdG8gcmlzZSBhbmQgbG93ZXItaW5jb21lIHJlc2lkZW50cyB0byBiZSBwcmljZWQgb3V0LiBXZSBpbmNvcnBvcmF0ZSBlbmRvZ2Vub3VzIGdlbnRyaWZpY2F0aW9uIGFzIHdlbGwgYXMgb3RoZXIgaW5kaWNhdG9ycyBvZiBnZW50cmlmaWNhdGlvbiwgbmVpZ2hib3Job29kIGFtZW5pdGllcyBhbmQgaW5mcmFzdHJ1Y3R1cmUgdG8gcHJlZGljdCB3aGljaCBhcmVhcyBvZiBQaGlsYWRlbHBoaWEgYXJlIG1vc3QgbGlrZWx5IHRvIGV4cGVyaWVuY2UgbmV3IGRldmVsb3BtZW50LiBJZiB0ZW5hbnRzIGNhbiB1bmRlcnN0YW5kIGhvdyBmYXIgaW50byB0aGUgZnV0dXJlIGRldmVsb3BtZW50IHByZXNzdXJlIGlzIGxpa2VseSB0byBwcm92aWRlIGdlbnRyaWZpY2F0aW9uIHByZXNzdXJlIGFuZCB3aGVyZSwgdGhleSBjYW4gYmV0dGVyIHRhcmdldCBhbnRpLWRpc3BsYWNlbWVudCBzdHJhdGVnaWVzLiBUaGlzIGFwcGxpY2F0aW9uIHdpbGwgaGVscCBub24tcHJvZml0IGFuZCB2b2x1bnRlZXItYmFzZWQgaG91c2luZyBqdXN0aWNlIG9yZ2FuaXphdGlvbnMgbWF4aW1pemUgdGVuYW50IG9yZ2FuaXppbmcgY2FwYWNpdHkgYW5kIHJlc291cmNlcyBmb3IgYW50aS1kaXNwbGFjZW1lbnQgYnkgZGlyZWN0aW5nIHBvbGljeSBtYWtlcnMgd2hlcmUgdG8gaW52ZXN0IGhvdXNpbmcgdHJ1c3QgZnVuZHMgJiBjb21tdW5pdHkgbGFuZCB0cnVzdHMuCgojIyMgVXNlIENhc2UKQ29tbXVuaXR5IGxhbmQgdHJ1c3RzIGFyZSBhbiBbb2Z0ZW4tY2l0ZWQgc3RyYXRlZ3kgZm9yIHByZXZlbnRpbmcgZGlzcGxhY2VtZW50XSggaHR0cHM6Ly9zbWFydGdyb3d0aGFtZXJpY2Eub3JnL3Jlc291cmNlcy9zdHJhdGVnaWVzLXRvLW1pbmltaXplLWRpc3BsYWNlbWVudC1jb21tdW5pdHktbGFuZC10cnVzdC8jOn46dGV4dD1PdmVyJTIwdGltZSUyQyUyMGNvbW11bml0eSUyMGxhbmQlMjB0cnVzdHMscmVhbCUyMGVzdGF0ZSUyMHZhbHVlcyUyMGlzJTIwcmVkdWNlZC4pLiBBIGNvbW11bml0eSBsYW5kIHRydXN0IGlzIGEgbG9uZy10ZXJtIHN0cmF0ZWd5IHRvIHByZXNlcnZlIGFmZm9yZGFibGUgaG91c2luZyBieSBwdXR0aW5nIHRoZSBsYW5kIG9mIHByb3BlcnR5IGluIGEgY29tbXVuaXR5LWNvbnRyb2xsZWQgdHJ1c3QgYW5kIG9ubHkgc2VsbGluZyB0aGUgc3RydWN0dXJlLiBCeSBzZXBhcmF0aW5nIHRoZSBsYW5kIGFuZCBwcm9wZXJ0eSBmcm9tIHRoZSBzdHJ1Y3R1cmUgKGFzIHdlbGwgYXMgaW1wb3NpbmcgZGVlZCByZXN0cmljdGlvbnMgZm9yIGFmZm9yZGFiaWxpdHkgaW5jbHVkaW5nIGEgcHJlZGV0ZXJtaW5lZCByZS1zYWxlIGZvcm11bGF0ZSkgZ3VhcmFudGVlcyBhZmZvcmRhYmlsaXR5IG92ZXIgdGhlIGxvbmcgdGVybS4gQWZmb3JkYWJpbGl0eSBtZWFzdXJlIGNhbiBoZWxwIGxvdy1pbmNvbWUgcmVzaWRlbnRzIGJ1aWxkIGVxdWl0eSB0aHJvdWdoIGhvbWVvd25lcnNoaXAgYW5kIHJldGFpbiBhZmZvcmRhYmxlIGhvdXNpbmcgZm9yIGZ1dHVyZSByZXNpZGVudHMuIEhvd2V2ZXIsIGNvbW11bml0eSBsYW5kIHRydXN0cyBbbGFjayBzdWZmaWNpZW50IGZ1bmRpbmddKCBodHRwczovL3d3dy5zaWdodGxpbmUub3JnLzIwMjEvMDgvMjMvaG93LWNhbi1jaXRpZXMtbW92ZS10aGUtbmVlZGxlLW9uLWNvbW11bml0eS1sYW5kLXRydXN0cy8pIHRvIHNjYWxlIHVwIHRvIG1lZXQgYWZmb3JkYWJsZSBob3VzaW5nIG5lZWRzIHdoZW4gZGVtYW5kIGlzIGhpZ2ggYWNyb3NzIHRoZSBib2FyZCAqKldpdGggbGltaXRlZCByZXNvdXJjZXMgYW5kIHdpZGVzcHJlYWQgbmVlZCwgdGFyZ2V0aW5nIGxhbmQgdHJ1c3QgZnVuZGluZyBpcyBjcml0aWNhbCB0byBwcmV2ZW50IGRpc3BsYWNlbWVudC4qKgoKIVtTb3VyY2U6IFZpcmdpbmlhIFN0YXRld2lkZSBDb21tdW5pdHkgTGFuZCBUcnVzdCwgaHR0cHM6Ly93d3cudnNjbHQub3JnL2ZhcS9dKENMVF9Nb2RlbC5wbmcpCgojIyMgV2Vic2l0ZSBNb2NrdXAKCmluY2x1ZGUgcGhvdG9zIG9mIG91ciBoeXBvdGhldGljYWwgd2Vic2l0ZSBhcHBsaWNhdGlvbiB0aGF0IGNvdWxkIGJlIHVzZWQgYXMgYSByZXVzbHQgb2YgdGhpcyBwcm9qZWN0LiAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKSAKb3B0aW9ucyhzY2lwZW49OTk5KQoKY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHsKICBpZiAoa25pdHI6OmlzX2xhdGV4X291dHB1dCgpKSB7CiAgICBzcHJpbnRmKCJcXHRleHRjb2xvcnslc317JXN9IiwgY29sb3IsIHgpCiAgfSBlbHNlIGlmIChrbml0cjo6aXNfaHRtbF9vdXRwdXQoKSkgewogICAgc3ByaW50ZigiPHNwYW4gc3R5bGU9J2NvbG9yOiAlczsnPiVzPC9zcGFuPiIsIGNvbG9yLAogICAgICB4KQogIH0gZWxzZSB4Cn0KCmxpYnJhcnkoTUFTUykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzZikKbGlicmFyeShzcGRlcCkKbGlicmFyeShjYXJldCkKbGlicmFyeShja2FucikKbGlicmFyeShnZ2NvcnJwbG90KSAjIHBsb3QgY29ycmVsYXRpb24gcGxvdApsaWJyYXJ5KHNmZGVwKQpsaWJyYXJ5KHRpZHljZW5zdXMpCmxpYnJhcnkoaHR0cikKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkocnBobCkKbGlicmFyeShnZW9qc29uaW8pCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShsZWhkcikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KHRpZ3JpcykKbGlicmFyeShwc3ljaCkKbGlicmFyeShvc21kYXRhKQpsaWJyYXJ5KHRpZ3JpcykKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KEZOTikKbGlicmFyeShncmlkKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShwc2NsKQpsaWJyYXJ5KG1wYXRoKQpsaWJyYXJ5KERUKQoKCnNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3VyYmFuU3BhdGlhbC9QdWJsaWMtUG9saWN5LUFuYWx5dGljcy1MYW5kaW5nL21hc3Rlci9mdW5jdGlvbnMuciIpCgojcGFsZXR0ZQoKcGFsZXR0ZTEgPC0gYygiIzJkMDBmNyIsICIjNmEwMGY0IiwgIiM4OTAwZjIiLCAiI2JjMDBkZCIsICIjZTUwMGE0IiwgIiNmMjAwODkiLCAiI2ZmYjYwMCIpCgojTG9hZCBuZWlnaGJvcmhvb2QgZGF0YQoKcGhsLm5oIDwtIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9hemF2ZWEvZ2VvLWRhdGEvbWFzdGVyL05laWdoYm9yaG9vZHNfUGhpbGFkZWxwaGlhL05laWdoYm9yaG9vZHNfUGhpbGFkZWxwaGlhLmdlb2pzb24iKSU+JQogICAgc3RfdHJhbnNmb3JtKGNycz00MzI2KSAlPiUKICBkcGx5cjo6c2VsZWN0KG5hbWUsIG1hcG5hbWUpCgpwaGwuY2QgPC0gc3RfcmVhZCgiaHR0cHM6Ly9vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzLzkyOThjMmYzZmEzMjQxZmJiMTc2ZmYxZTg0ZDMzMzYwXzAuZ2VvanNvbiIpJT4lCiAgICBzdF90cmFuc2Zvcm0oY3JzPTQzMjYpIAoKIyBMb2FkIHBlcm1pdCBkYXRhCgojICMgVVJMCgojIGNhcnRvX3VybCA9ICJodHRwczovL3BobC5jYXJ0by5jb20vYXBpL3YyL3NxbCIKIyAKIyAjIHBlcm1pdCBkYXRhCiMgdGFibGVfbmFtZSA9ICJwZXJtaXRzIgojIAojICMgcXVlcnkKIyB3aGVyZSA9ICJjb21tZXJjaWFsb3JyZXNpZGVudGlhbCA9ICdSRVNJREVOVElBTCciCiMgCiMgcXVlcnkgPSBwYXN0ZSgiU0VMRUNUICosIFNUX1kodGhlX2dlb20pIEFTIGxhdCwgU1RfWCh0aGVfZ2VvbSkgQVMgbG5nIiwKIyAgICAgICAgICAgICAgICJGUk9NIiwgdGFibGVfbmFtZSwKIyAgICAgICAgICAgICAgICJXSEVSRSIsIHdoZXJlKQojIAojIHJlc19wZXJtaXRzID0gcnBobDo6Z2V0X2NhcnRvKHF1ZXJ5LCBmb3JtYXQgPSAiY3N2IiwgYmFzZV91cmwgPSBjYXJ0b191cmwsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKcmVzX3Blcm1pdHMgPC0gcmVhZC5jc3YoIkRhdGEvcmVzX3Blcm1pdHNfMTEuMjUuY3N2IikKCmBgYAoKIyMgKipNRVRIT0RPTE9HWSoqCgpVc2luZyBQaGlsYWRlbHBoaWEgcGVybWl0IGRhdGEgYXMgYSBjYXNlIHN0dWR5LCB3ZSBjcmVhdGVkIGEgUG9pc3NvbiBtb2RlbCB0byBwcmVkaWN0IHdoZXJlIGRldmVsb3BtZW50IGlzIG1vc3QgbGlrZWx5IHRvIG9jY3VyLiBXZSBkZXZlbG9wZWQgYSBzcGF0aWFsIGxhZyBmb3IgUGhpbGFkZWxwaGlhIG5laWdoYm9yaG9vZHMgdG8gcHJlZGljdCBuZXcgY29uc3RydWN0aW9uIHBlcm1pdCBpc3N1YW5jZS4gT25lIFtzdHVkeV0oaHR0cHM6Ly9yZXNlYXJjaHJlcG9zaXRvcnkud3Z1LmVkdS9jZ2kvdmlld2NvbnRlbnQuY2dpP2FydGljbGU9OTAwNSZjb250ZXh0PWV0ZCkgZXhhbWluZWQgV2FzaGluZ3RvbiBELkMuLCB3ZWlnaGluZyBwZXJtaXQgcGF0dGVybnMgYXMgcG90ZW50aWFsIGluZGljYXRvcnMgb2YgZGV2ZWxvcG1lbnQuIFRoaXMgc3R1ZHkgaWRlbnRpZmllZCBhbmQgYXNzaWduZWQgd2VpZ2h0cyB0byB0aGUgZm9sbG93aW5nIHBlcm1pdCB0eXBlczoKLSBBZGRpdGlvbi9BZGRpdGlvbiwgQWx0ZXJhdGlvbiwgYW5kIFJlcGFpci9BbHRlcmF0aW9uIGFuZCBSZXBhaXIKLSBFbGVjdHJpYy9FbGVjdHJpYyBHZW5lcmFsL0VsZWN0cmljIEhlYXZ5IFVwCi0gUGx1bWJpbmcvUGx1bWJpbmcgYW5kIEdhcwotIFJhemUKLSBOZXcgQnVpbGRpbmcKLSBEZW1vbGl0aW9uCi0gTWVjaGFuaWNhbAotIENlcnRpZmljYXRlIG9mIE9jY3VwYW5jeQoKV2UgZXhhbWluZWQgdGhlIFBoaWxhZGVscGhpYSBwZXJtaXQgZGF0YXNldCBhbmQgZmlsdGVyZWQgZm9yIHJlc2lkZW50aWFsIGNvbnN0cnVjdGlvbiBwZXJtaXRzLiBXZSB0aGVuICBsb29rZWQgYXQgcG9wdWxhdGlvbiBsZXZlbCBkYXRhIFtpbmRpY2F0b3JzIG9mIGdlbnRyaWZpY2F0aW9uXShodHRwczovL2RyZXhlbC5lZHUvdWhjL3Jlc291cmNlcy9icmllZnMvTWVhc3VyZS1vZi1HZW50cmlmaWNhdGlvbi1mb3ItVXNlLWluLUxvbmdpdHVkaW5hbC1QdWJsaWMtSGVhbHRoLVN0dWRpZXMtaW4tdGhlLVVTLykgc3VjaCBhcyAqKnZhY2FuY3ksIHJlc2lkZW50cyB3aXRoIGJhY2hlbG9y4oCZcyBkZWdyZWVzLCBjaGFuZ2VzIGluIHJlbnRzLCBjaGFuZ2VzIGluIGhvbWUgdmFsdWVzLCBhbmQgbWVkaWFuIGluY29tZSoqIHRvIGFzc2lnbiBhbiBpbmRpY2F0b3IgdG8gZWFjaCBDZW5zdXMgdHJhY3QuIEluIGFyZWFzIHdpdGggZGVjcmVhc2luZyB2YWNhbmN5IGFuZCBpbmNyZWFzaW5nIGJhY2hlbG9y4oCZcyBkZWdyZWVzICYgaW5jb21lcywgd2UgaGF2ZSB1c2VkIHRoZXNlIGFzIGdlbnRyaWZpY2F0aW9uIGluZGljYXRvcnMgaW5kZXBlbmRlbnQgb2YgZGV2ZWxvcG1lbnQuIFdlIHRoZW4gZXhhbWluZWQgd2hpY2ggcGVybWl0IHR5cGVzIGFyZSBtb3N0IHN0cm9uZ2x5IGFzc29jaWF0ZWQgd2l0aCBnZW50cmlmaWNhdGlvbiBvciBpbnRlbnNlIGdlbnRyaWZpY2F0aW9uLCBwYXJ0aWN1bGFybHkgd2hlcmUgdGhlcmUgaXMgYSBsYXJnZSBkaWZmZXJlbmNlIGluIHRoZSBtZWFuIG51bWJlciBvZiBwZXJtaXRzIGluIGdlbnRyaWZ5aW5nIGFyZWFzIGFzIG9wcG9zZWQgdG8gbm9uLWdlbnRyaWZ5aW5nIG9yIGluZWxpZ2libGUgYXJlYXMgKHdoZXJlIGluY29tZSBhbmQgaG9tZSB2YWx1ZXMgYXJlIGFscmVhZHkgaGlnaCkuCgpXZSBlbmdpbmVlcmVkIG90aGVyIGZlYXR1cmVzIGluY2x1ZGluZyBwcm94aW1pdHkgdG8gZGVzaXJhYmxlIGFtZW5pdGllcywgc3VjaCBhcyBwdWJsaWMgdHJhbnNpdCwgY2FmZXMsIGFuZCBqb2JzLiBXZSBjcmVhdGUgc3BhdGlhbCBhbmQgdGVtcG9yYWwgbGFncyBmb3IgbWFueSBvZiBvdXIgZGVtb2dyYXBoaWMgdmFyaWFibGVzLCBsb29raW5nIGF0IHRoZSB2YWx1ZXMgb2YgbmVpZ2hib3JpbmcgY2Vuc3VzIHRyYWN0cyBhbmQgdGhlIHZhbHVlcyBmcm9tIHRoZSBwcmlvciAoMjAxNykgNS15ZWFyIEFtZXJpY2FuIENvbW11bml0eSBTdXJ2ZXkuCgpXZSB0aGVuIHJ1biBzaXggUG9pc3NvbiBsaW5lYXIgcmVncmVzc2lvbnMgdXNpbmcgdHdvIGRpZmZlcmVudCBzZXRzIG9mIHZhcmlhYmxlcyBhbmQgdGhyZWUgY3Jvc3MtdmFsaWRhdGlvbiBzdHJhdGVnaWVzLiBXZSBleGFtaW5lIHRoZSBlcnJvcnMgaW4gZGV0YWlsIHRvIGlkZW50aWZ5IHRoZSBzdHJvbmdlc3QgbW9kZWwuCgojIyAqKkRFUEVOREVOVCBWQVJJQUJMRSoqCgpJbiB0aGlzIHNlY3Rpb24sIHdlIGludmVzdGlnYXRlIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgcHJpb3IgdG8gYW55IGZpbHRlcmluZy4KCk91ciBkZXBlbmRlbnQgdmFyaWFibGUgaXMgdGhlIG51bWJlciBvZiBwZXJtaXRzIGlzc3VlZCBwZXIgY2Vuc3VzIHRyYWN0LiBUaGUgdW5pdCBvZiBhbmFseXNpcyBpcyBwZXJtaXRzIHBlciBjZW5zdXMgdHJhY3QgYmVjYXVzZSBvZiBjZW5zdXMgZGF0YSBhdmFpbGFiaWxpdHkgYW5kIHRoZSBmYWN0IHRoYXQgZmVkZXJhbCBhbmQgc3RhdGUgZnVuZGluZyBvZnRlbiBmbG93cyB0byBhcmVhcyB2aWEgY2Vuc3VzIHRyYWN0IG9yIHppcCBjb2RlIGVsaWdpYmlsaXR5LiBXZSB0aW1lIGFuZCBvciBzcGFjZSB0byBzZWUgaWYgdGhlcmUgdGhlcmUgYXJlIGFueSBwYXR0ZXJucyB0byBtaXNzaW5nIHBlcm1pdCBkYXRhIHNvIGFzIHRvIGJldHRlciBkZXRlcm1pbmUgd2hpY2ggeWVhcnMgdG8gdGVzdCBhbmQgdHJhaW4gb24uIAoKYGBge3J9CiMgU2VlIGlmIHRoZXJlIGFyZSBhbnkgcGF0dGVybnMgdG8gbWlzc2luZyBzcGF0aWFsIGRhdGEKCnJlc19wZXJtaXRzICU+JQogIGdyb3VwX2J5KHllYXIgPSB5ZWFyKGFzLkRhdGUocGVybWl0aXNzdWVkYXRlKSkpICU+JQogIHN1bW1hcmlzZV9hbGwoZnVucyhzdW0oaXMubmEoLikpKSkgJT4lCmRhdGF0YWJsZShvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMTIpLAogICAgICAgICAgICBzdHlsZSA9ICJhdXRvIikKCmBgYAoKV2hlbiBsb29raW5nIGF0IGNvdW5jaWwgZGlzdHJpY3QgdnMuIHllYXIgZGlzdHJpYnV0aW9uIG9mIHBlcm1pdCBkYXRhLCB0aGVyZSBhcmUgcGxlbnR5IG9mIHBlcm1pdHMgaW4gZWFjaCBkaXN0cmljdCwgYnV0IHdpZGUgZGlmZmVyZW5jZXMgYnkgeWVhci4gUGVybWl0dGluZyBhY3Rpdml0eSBpbiBlYXJsaWVyIHllYXJzIGlzIG11Y2ggbG93ZXIgdGhhbiAyMDIwIHRvIDIwMjMuIEFsdGhvdWdoIDIwMjAgaXMgdHlwaWNhbGx5IGNvbnNpZGVyZWQgYW4gYW5vbWFseSwgcGVybWl0IGlzc3VhbmNlIGFuZCBkZXZlbG9wbWVudCB3YXMgc3Ryb25nIGR1ZSB0byBsb3cgaW50ZXJlc3QgcmF0ZXMuIFRoZXJlZm9yZSwgd2UgdHJhaW5lZCAyMDIwIGFuZCAyMDIxIHBlcm1pdCBpc3N1YW5jZSBkYXRhIHRvIHRlc3Qgb24gMjAyMiBkZXZlbG9wbWVudCBhY3Rpdml0eS4gQXMgYSByZXN1bHQsIHdlIGdvdCByaWQgb2YgbWlzc2luZyBzcGF0aWFsIGRhdGEKCmBgYHtyfQpyZXNfcGVybWl0cyAlPiUKICBncm91cF9ieShjb3VuY2lsX2Rpc3RyaWN0KSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogIGdncGxvdChhZXMoeSA9IGFzLmZhY3Rvcihjb3VuY2lsX2Rpc3RyaWN0KSwgeCA9IGNvdW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIiNmZmI2MDAiKSArIAogIHBsb3RUaGVtZSgpICsgCiAgbGFicyh0aXRsZSA9ICJQZXJtaXRzIGJ5IENvdW5jaWwgRGlzdHJpY3QiLCBjYXB0aW9uID0gIkZpZ3VyZSAxIikKCnJlc19wZXJtaXRzICU+JQogIGdyb3VwX2J5KHllYXIgPSB5ZWFyKGFzLkRhdGUocGVybWl0aXNzdWVkYXRlKSkpICU+JQogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogIGdncGxvdChhZXMgKHggPSB5ZWFyLCB5ID0gY291bnQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiI2ZmYjYwMCIpICsKICBwbG90VGhlbWUoKSArIAogIGxhYnModGl0bGUgPSAiUGVybWl0cyBieSBZZWFyIiwgY2FwdGlvbiA9ICJGaWd1cmUgMiIpCgpyZXNfcGVybWl0cyA8LSByZXNfcGVybWl0cyAlPiUKICBmaWx0ZXIoIWlzLm5hKGxuZykgJiAhaXMubmEobGF0KSkgJT4lCiAgc3RfYXNfc2YoY29vcmRzID0gYygibG5nIiwibGF0IiksY3JzPTQzMjYpCgpgYGAKCk5leHQsIHdlIGV4YW1pbmVkIHBlcm1pdCB0eXBlcyBhbmQgY291bnRzIHRvIHVuZGVyc3RhbmQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwZXJtaXQgdHlwZXMgc3BlY2lmaWMgdG8gUGhpbGFkZWxwaGlhLiBXaGVuIGxvb2tpbmcgYXQgdGhlIGNvdW50IG9mIHBlcm1pdCB0eXBlcywgYXMgcmVwcmVzZW50ZWQgYmVsb3cgYnkgbiwgd2UgY2FuIHNlZSB0aGF0IHRoZSBwZXJtaXQgdHlwZSBhbG9uZSBkb2VzIG5vdCBpbmRpY2F0ZSB3aGF0IHR5cGUgb2Ygd29yayBpcyBiZWluZyBkb25lLiBOZXcgY29uc3RydWN0aW9uIHdpbGwgbmVjZXNzaXRhdGUgZGlmZmVyZW50IHBlcm1pdCB0eXBlcywgc3VjaCBhcyBwbHVtYmluZywgbWVjaGFuaWNhbCwgb3IgZWxlY3RyaWNhbCBwZXJtaXRzLCBidXQgdGhlc2UgY2FuIGFsc28gYmUgY291bnRlZCBpbiByZWd1bGFyIG1haW50ZW5hbmNlLiBUaGVyZWZvcmUsIGZpbHRlcmluZyBvbmx5IGJhc2VkIG9uIG5ldyBjb25zdHJ1Y3Rpb24gbWF5IGJlIG1pc2xlYWRpbmcuIFdlIGxvb2tlZCBhdCBjZW5zdXMgZGF0YSBuZXh0IHRvIGJlZ2luIHVuZGVyc3RhbmRpbmcgaG93IHBlcm1pdCBkYXRhIGlzIGFzc29jaWF0ZWQgd2l0aCBkZW1vZ3JhcGhpY3MgYW5kIG91ciBnZW50cmlmaWNhdGlvbiBpbmRpY2F0b3IuIAoKYGBge3Igd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIH0KcmVzX3Blcm1pdHMgJT4lCiAgZ3JvdXBfYnkocGVybWl0ZGVzY3JpcHRpb24sIHR5cGVvZndvcmspICU+JQogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSkgJT4lCiAgYXJyYW5nZShwZXJtaXRkZXNjcmlwdGlvbiwgZGVzYyhuKSkgJT4lCiAgZGF0YXRhYmxlKG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAxMCksCiAgICAgICAgICAgIHN0eWxlID0gImF1dG8iKQoKCgpgYGAKCmBgYHtyIHJlc3VsdHMgPSAnaGlkZSd9CnBobF90cmFjdHM0MzI2IDwtIHRyYWN0cyhzdGF0ZSA9ICJQQSIsCiAgICAgICAgICAgICAgICAgICAgIGNvdW50eSA9ICJQaGlsYWRlbHBoaWEiLAogICAgICAgICAgICAgICAgICAgICB5ZWFyID0gIjIwMjAiKSAlPiUKICBzZWxlY3QoR0VPSUQsIGdlb21ldHJ5KSAlPiUKICBzdF9zZXRfY3JzKDQzMjYpCgpyZXNfcGVybWl0czIgPC0gcmVzX3Blcm1pdHMgJT4lCiAgZ3JvdXBfYnkocGVybWl0ZGVzY3JpcHRpb24sIHR5cGVvZndvcmspICU+JQogIG11dGF0ZShuID0gbigpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KHBlcm1pdHR5cGUsIHBlcm1pdGRlc2NyaXB0aW9uLCB0eXBlb2Z3b3JrLCBnZW9tZXRyeSwgcGVybWl0aXNzdWVkYXRlKSAlPiUKICAjam9pbiB0byBjZW5zdXMgdHJhY3QKICBzdF9qb2luKHBobF90cmFjdHM0MzI2KQpgYGAKCldoZW4gd2UgbG9vayBhdCBudW1iZXIgb2YgcGVybWl0cyBieSB0eXBlIHBlciB0cmFjdCBhcyBzZWVuIGJlbG93LCBpdCBiZWNvbWVzIGFwcGFyZW50IHRoZXJlIGFyZSBhIHZlcnkgaGlnaCBjb25jZW50cmF0aW9uIG9mIGFsbCBwZXJtaXRzIGluIHRoZSBGaXNodG93biwgUG9ydCBSaWNobW9uZCBhcmVhLiBXaGVuIHdlIGxvb2sgYnkgeWVhciwgd2Ugc2VlIHRoYXQgc29tZSBvZiB0aGlzIGlzIGJlY2F1c2Ugb2YgZGF0YSB1bmF2YWlsYWJpbGl0eSBpbiBlYXJsaWVyIHllYXJzIGZvbGxvd2VkIGJ5IGEgZmx1eCBvZiBwZXJtaXQgaXNzdWFuY2UgZnJvbSAyMDIwIHRvIDIwMjIuIAoKU28gd2hhdCBoYXBwZW5lZCB0aGVzZSB5ZWFycz8gCgotIEludGVyZXN0IHJhdGVzIHdlcmUgYXQgYSBoaXN0b3JpY2FsIGxvdyBpbiAyMDIwIHRvIDIwMjIsIGluIHBhcnQgdG8gZW50aWNlIGVjb25vbWljIGFjdGl2aXR5IHRvIGJlIGFzIGVudGljaW5nIGFzIHBvc3NpYmxlIGFmdGVyIHRoZSBDT1ZJRC0xOSBwYW5kZW1pYyBicm91Z2h0IHRoZSBlY29ub215IHRvIGEgc2NyZWVjaGluZyBoYWx0LiAKIVtBdmVyYWdlIGludGVyZXN0IFJhdGVzIDIwMTcgLSAyMDIzLCBTb3VyY2U6IGh0dHBzOi8vdGFrZS1wcm9maXQub3JnL2VuL3N0YXRpc3RpY3MvaW50ZXJlc3QtcmF0ZS91bml0ZWQtc3RhdGVzL10oaW50ZXJlc3QtcmF0ZXMucG5nKQoKLSBNb3JlIGxvY2FsbHksIFBoaWxhZGVscGhpYSBwYXNzZWQgYSB0YXggcG9saWN5IGluIDIwMDAgdG8gcHJvdmlkZSBhIHRheCBhYmF0ZW1lbnQgb24gbmV3IGhvdXNpbmcgY29uc3RydWN0aW9uIHVudGlsIFtpdCBleHBpcmVkIGluIDIwMjJdKGh0dHBzOi8vd3d3LmlucXVpcmVyLmNvbS9uZXdzL3BoaWxhZGVscGhpYS9pbnEyL3BoaWxhZGVscGhpYS1wcm9wZXJ0eS10YXgtYWJhdGVtZW50LW5laWdoYm9yaG9vZC1tYXBzLWRhdGEtMjAyMzAyMTQuaHRtbCkuIFRoZSBwb2xpY3kgZ29hbCB3YXMgdG8gc3B1ciBkZXZlbG9wbWVudCBmb3IgcmVzaWRlbnRpYWwgcHJvcGVydHkgb3duZXJzIGJ5IHByb3ZpZGluZyBhIDEwLXllYXIgdGF4IGFiYXRlbWVudCBvbiB0aGUgdmFsdWUgb2YgaW1wcm92ZW1lbnRzLiBUaGlzIHBhcnRpYWxseSBleHBsYWlucyB0aGUgdGltZSBsYWcgb2Ygc29tZSBkZXZlbG9wbWVudCwgYnV0IGl0IHJlbWFpbnMgdW5jbGVhciB3aHkgdGhlIGRldmVsb3BtZW50IHdhcyBzbyBjb25jZW50cmF0ZWQgc3BhdGlhbGx5LiAKCk91ciBuZXh0IGRhdGEgYW5hbHlzaXMgc3RlcCBpcyB0byBkcm9wIHRoZSB5ZWFyIGFuZCBsb29rIG9ubHkgYXQgcGVybWl0cyBpbiBhbmQgYWZ0ZXIgMjAxOC4gSXQgaXMgbGlrZWx5IHRoYXQgbW9yZSBwZXJtaXRzIGFyZSBpc3N1ZWQgaW4gYXJlYXMgd2hlcmUgdGhlc2UgY2hhbmdlcyBhcmUgYWxyZWFkeSBoYXBwZW5pbmcuIAoKYGBge3J9CgojIFBJQ0sgMQoKdHJhY3RfcGVybWl0c195ZWFyIDwtIHJlc19wZXJtaXRzMiAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgbXV0YXRlKHBlcm1pdCA9IHBhc3RlKHBlcm1pdGRlc2NyaXB0aW9uLCAiLSIsIHR5cGVvZndvcmspLAogICAgICAgICB5ZWFyID0geWVhcihhcy5EYXRlKHBlcm1pdGlzc3VlZGF0ZSkpKSAlPiUKICBzZWxlY3QoLXBlcm1pdGlzc3VlZGF0ZSwgLXBlcm1pdGRlc2NyaXB0aW9uLCAtcGVybWl0dHlwZSwgLXR5cGVvZndvcmspICU+JQogIGdhdGhlcihWYXJpYWJsZSwgdmFsdWUsIC1HRU9JRCwgLXllYXIpICU+JQogIGRwbHlyOjpjb3VudChWYXJpYWJsZSwgdmFsdWUsIEdFT0lELCB5ZWFyKSAlPiUKICBncm91cF9ieShHRU9JRCwgeWVhcikgJT4lCiAgbXV0YXRlKFRPVEFMID0gc3VtKG4pKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc3ByZWFkKGtleSA9IHZhbHVlLCB2YWx1ZSA9IG4pICU+JQogIHNlbGVjdCgtVmFyaWFibGUpICU+JQogIHJlcGxhY2UoaXMubmEoLiksIDApCgojdG90YWwgcGVybWl0cwp0cmFjdF9wZXJtaXRzX3llYXIgJT4lCiAgbGVmdF9qb2luKHBobF90cmFjdHM0MzI2KSAlPiUKICBzdF9zZigpICU+JQogIGZpbHRlcih5ZWFyID4gMjAxNCAmIHllYXIgPCAyMDIzKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX3NmKGFlcyhmaWxsID0gY3V0KFRPVEFMLCBicmVha3MgPSA4KSkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMSkgKwogIG1hcFRoZW1lKCkgKwogIGxhYnModGl0bGUgPSAiUGVybWl0cyBiZXR3ZWVuIDIwMTQgLSAyMDIzIiwgY2FwdGlvbiA9ICJGaWd1cmUgMyIpCgoKI2J5IHllYXIgLSB0aGVyZSBhcmUgYSBsb3Qgb2YgdHJhY3RzIG1pc3NpbmcgaW4gZWFybGllciB5ZWFycwojIHRyYWN0X3Blcm1pdHNfeWVhciAlPiUKIyAgIGxlZnRfam9pbihwaGxfdHJhY3RzNDMyNikgJT4lCiMgICBzdF9zZigpICU+JQojICAgZmlsdGVyKHllYXIgPiAyMDE0ICYgeWVhciA8PSAyMDIzKSAlPiUKIyAgIGdncGxvdCgpKwojICAgZ2VvbV9zZihhZXMoZmlsbCA9IFRPVEFMKSkgKwojICAgc2NhbGVfZmlsbF9iaW5uZWQobi5icmVha3M9OCkgKwojICAgZmFjZXRfd3JhcCh+eWVhcikgKwojICAgbWFwVGhlbWUoKQoKYGBgCgoKCmBgYHtyfQp0cmFjdF9wZXJtaXRzIDwtIHJlc19wZXJtaXRzMiAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgbXV0YXRlKHBlcm1pdCA9IHBhc3RlKHBlcm1pdGRlc2NyaXB0aW9uLCAiLSIsIHR5cGVvZndvcmspLAogICAgICAgICB5ZWFyID0geWVhcihhcy5EYXRlKHBlcm1pdGlzc3VlZGF0ZSkpKSAlPiUKICBmaWx0ZXIoeWVhciA+PSAyMDE4KSAlPiUKICBzZWxlY3QoLXBlcm1pdGlzc3VlZGF0ZSwgLXBlcm1pdGRlc2NyaXB0aW9uLCAtcGVybWl0dHlwZSwgLXR5cGVvZndvcmssIC15ZWFyKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIHZhbHVlLCAtR0VPSUQpICU+JQogIGRwbHlyOjpjb3VudChWYXJpYWJsZSwgdmFsdWUsIEdFT0lEKSAlPiUKICBncm91cF9ieShHRU9JRCkgJT4lCiAgbXV0YXRlKFRPVEFMID0gc3VtKG4pKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc3ByZWFkKGtleSA9IHZhbHVlLCB2YWx1ZSA9IG4pICU+JQogIHNlbGVjdCgtVmFyaWFibGUpICU+JQogIHJlcGxhY2UoaXMubmEoLiksIDApCgpgYGAKCiMjICoqUEVSTUlUIFNFTEVDVElPTioqCgojIyMgQ2Vuc3VzIERhdGEKCkJ1dCBhIGtleSBjb25zaWRlcmF0aW9uIGlzOiBbZG9lcyBkZW1hbmQgbGVhZCBzdXBwbHldKGh0dHBzOi8vam91cm5hbHMuc2FnZXB1Yi5jb20vZG9pL2Z1bGwvMTAuMTE3Ny8wMDQyMDk4MDIwOTQwNTk2KSBvciB2aWNlIHZlcnNhPyBXaGljaCBjb21lcyBmaXJzdCwgZGV2ZWxvcG1lbnQgb3IgZ2VudHJpZmljYXRpb24/CgpUaGVyZSBhcmUgdHdvIHRoZW9yaWVzIG9mIG5laWdoYm9yaG9vZCBjaGFuZ2UgYXMgaXQgcmVsYXRlcyB0byB0aGlzLiAKLSAxKSBIb3VzaW5nIGRlbWFuZCBvZiBtaWRkbGUtY2xhc3MsIG1vc3RseSB3aGl0ZSByZXNpZGVudHMgZW5jb3VyYWdlcyB0aGVtIHRvIGxlYXZlIG5laWdoYm9yaG9vZHMgdG8gc2VlayBvdXQgbW9yZSBhZmZvcmRhYmxlIG5laWdoYm9yaG9vZHMuIAotIDIpIERldmVsb3BtZW50IHN1cHBseSBvZiBuZXcgY29uc3RydWN0aW9uIGluIGhpc3RvcmljYWxseSBwb29yZXIgbmVpZ2hib3Job29kcyBlbmNvdXJhZ2VzIGFuIGluZmx1eCBvZiBuZXcgcmVzaWRlbnRzLCB3aG8gYXJlIGVuY291cmFnZWQgdG8gbW92ZSB0byBhIG5laWdoYm9yaG9vZCBieSB0aGUgc3VwcGx5IG9mIGhvdXNpbmcuIAoKVGhlIGFib3ZlIHN0dWR5IGZvdW5kIHRoYXQgaG91c2luZyBtYXJrZXQgZ3Jvd3RoIChpLmUuIG5ldyBwZXJtaXQgaXNzdWFuY2UpIGRpZCBub3QgcHJlZGljdCBmdXR1cmUgaW5jcmVhc2UgaW4gZ2VudHJpZmljYXRpb247IHJhdGhlciwgZGV2ZWxvcG1lbnQgZm9sbG93ZWQgbmVpZ2hib3Job29kIGNoYW5nZS4gR2VudHJpZmljYXRpb24gZmVhdHVyZXMgdGhlbXNlbHZlcyBjb3VsZCBiZSBjb25zaWRlcmVkIGluZGljYXRvcnMgb2YgZnV0dXJlIGRldmVsb3BtZW50LiAKCkFzIGEgcmVzdWx0IG9mIHBlcm1pdCBhbmFseXNpcywgd2Ugc2VsZWN0ZWQgcGVybWl0cyB0aGF0IHdlcmUgYXNzb2NpYXRlZCB3aXRoIGdlbnRyaWZpY2F0aW9uIGluZGljYXRvcnMuIApVc2luZyBbcHJpb3IgcmVzZWFyY2ggb24gZ2VudHJpZmljYXRpb24gaW5kaWNhdG9yc10oaHR0cHM6Ly9yZXNlYXJjaHJlcG9zaXRvcnkud3Z1LmVkdS9jZ2kvdmlld2NvbnRlbnQuY2dpP2FydGljbGU9OTAwNSZjb250ZXh0PWV0ZCkgYW5kIHVzaW5nIHB1YmxpY2x5IGF2YWlsYWJsZSBjZW5zdXMgZGF0YSwgd2UgZXhhbWluZWQgdGhlIGZvbGxvd2luZyBkZW1vZ3JhcGhpYyBjaGFyYWN0ZXJpc3RpY3M6IAoKLSBBbGwgQ2Vuc3VzIHRyYWN0cyBpbiBQaGlsYWRlbHBoaWEsIDVZIEFDUyBmb3IgMjAyMSBhbmQgZm9yIDIwMTYKLSBNZWRpYW4gSEggaW5jb21lLCBDUEktaW5mbGF0aW9uIGFkanVzdGVkCi0gTWVkaWFuIGhvbWUgdmFsdWUsIENQSS1pbmZsYXRpb24gYWRqdXN0ZWQKLSBNZWRpYW4gcmVudCwgQ1BJLWluZmxhdGlvbiBhZGp1c3RlZAotIE1lZGlhbiB5ZWFyIHN0cnVjdHVyZSBidWlsdAotIFRlbnVyZSAtIG93bmVyLW9jY3VwaWVkCi0gTnVtYmVyIG9mIGhvdXNpbmcgdW5pdHMKLSBOdW1iZXIgb2YgdmFjYW5jaWVzCi0gQ291bnQgb2Ygc2Vjb25kIG1vcnRnYWdlcwotIFBlcmNlbnQgd2hpdGUgcG9wdWxhdGlvbgotIFBlcmNlbnQgd2l0aCBCYWNoZWxvcidzIGRlZ3JlZQoKV2UgY2FsY3VsYXRlZDogcmVudCB0byBpbmNvbWUgcmF0aW8sIG5ldCBjaGFuZ2VzIG9mIG11bHRpcGxlIGluZGljYXRvcnMgZnJvbSAyMDE3IHRvIDIwMjIsIGFuZCBwZXJjZW50IGNoYW5nZXMgZnJvbSAyMDE3IHRvIDIwMjIuIE1lZGlhbiByZW50cywgdmFsdWVzLCBhbmQgaW5jb21lcyB3ZXJlIGFkanVzdGVkIHRvIDIwMjIgZG9sbGFycy4KCmBgYHtyIHJlc3VsdHMgPSAnaGlkZSd9CgpDUEkyMDE3IDwtIDI0Mi44MzkKQ1BJMjAyMiA8LSAyODEuMTQ4CgpDUEkgPC0gQ1BJMjAyMi9DUEkyMDE3CgojIE5PVEU6IGN1cnJlbnRseSByZXBsYWNpbmcgTkEgdmFsdWVzIHdpdGggYSB2ZXJ5IHNtYWxsIHZhbHVlLi4uCgpwaGxDZW5zdXMyMiA8LSAKICBnZXRfYWNzKGdlb2dyYXBoeSA9ICJ0cmFjdCIsIAogICAgICAgICAgdmFyaWFibGVzID0gYygiQjAxMDAzXzAwMSIsICJCMTkwMTNfMDAxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICJCMDIwMDFfMDAyIiwgIkIyNTA3N18wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAiQjI1MDY0XzAwMSIsICJCMjUwMzVfMDAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkIyNTAwM18wMDEiLCAiQjI1MDAzXzAwMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJCMjUwMDFfMDAxIiwgIkIyNTAwMl8wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAiQjI1MDAyXzAwMyIsICJCMjUwODFfMDAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkIyNTA4MV8wMDQiLCAiQjE1MDAzXzAwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJCMTUwMDNfMDIyIgogICAgICAgICAgICAgICAgICAgICAgICApLCAKICAgICAgICAgIHllYXIgPSAyMDIyLCAKICAgICAgICAgIGRhdGFzZXQgPSAiYWNzNSIsCiAgICAgICAgICBzdGF0ZSA9ICJQQSIsIAogICAgICAgICAgZ2VvbWV0cnkgPSBGQUxTRSwgCiAgICAgICAgICBjb3VudHkgPSBjKCJQaGlsYWRlbHBoaWEiKSwKICAgICAgICAgIG91dHB1dCA9ICJ3aWRlIikgJT4lCiAgcmVuYW1lKFRvdGFsX1BvcCA9ICBCMDEwMDNfMDAxRSwKICAgICAgICAgTWVkX0luYyA9IEIxOTAxM18wMDFFLAogICAgICAgICBXaGl0ZV9Qb3AgPSBCMDIwMDFfMDAyRSwKICAgICAgICAgTWVkX1ZhbHVlID0gQjI1MDc3XzAwMUUsCiAgICAgICAgIE1lZF9SZW50ID0gQjI1MDY0XzAwMUUsCiAgICAgICAgIE1lZF9TdHJ1Y3R1cmUgPSBCMjUwMzVfMDAxRSwKICAgICAgICAgT3duZXJfdW5pdiA9IEIyNTAwM18wMDFFLAogICAgICAgICBPd25lcnMgPSBCMjUwMDNfMDAyRSwKICAgICAgICAgVG90X1VuaXRzID0gQjI1MDAxXzAwMUUsCiAgICAgICAgIFZhY191bml2ID0gQjI1MDAyXzAwMUUsCiAgICAgICAgIFZhY2FudHMgPSBCMjUwMDJfMDAzRSwKICAgICAgICAgTW9ydF9zdGF0dXNfdW5pdiA9IEIyNTA4MV8wMDFFLAogICAgICAgICBTZWNfTW9ydCA9IEIyNTA4MV8wMDRFLAogICAgICAgICBCYWNoX3VuaXYgPSBCMTUwMDNfMDAxRSwKICAgICAgICAgQmFjaCA9IEIxNTAwM18wMjJFCiAgICAgICAgICkgJT4lCiAgZHBseXI6OnNlbGVjdChUb3RhbF9Qb3AsIE1lZF9JbmMsIFdoaXRlX1BvcCwKICAgICAgICAgICAgICAgIE1lZF9WYWx1ZSwgTWVkX1JlbnQsIE1lZF9TdHJ1Y3R1cmUsIAogICAgICAgICAgICAgICAgT3duZXJfdW5pdiwgT3duZXJzLCBUb3RfVW5pdHMsCiAgICAgICAgICAgICAgICBWYWNfdW5pdiwgVmFjYW50cywgTW9ydF9zdGF0dXNfdW5pdiwgCiAgICAgICAgICAgICAgICBTZWNfTW9ydCwgQmFjaF91bml2LCBCYWNoLCAgR0VPSUQpICU+JQogIG11dGF0ZShQZXJjZW50X1doaXRlID0gV2hpdGVfUG9wIC8gVG90YWxfUG9wLAogICAgICAgICBQZXJjZW50X093bmVyID0gT3duZXJzIC8gT3duZXJfdW5pdiwKICAgICAgICAgUGVyY2VudF9WYWNhbnQgPSBWYWNhbnRzIC8gVmFjX3VuaXYsCiAgICAgICAgIFBlcmNlbnRfMm1vcnQgPSBTZWNfTW9ydCAvIE1vcnRfc3RhdHVzX3VuaXYsCiAgICAgICAgIFBlcmNlbnRfYmFjaCA9IEJhY2ggLyBCYWNoX3VuaXYsCiAgICAgICAgIFJlbnRfSW5jb21lX1JhdGlvID0gTWVkX1JlbnQgLyAoTWVkX0luYy8xMikKICAgICAgICAgKSAlPiUKICAjcmVwbGFjZShpcy5uYSguKSwgLjAwMDAwMDAxKSAlPiUKICBzZWxlY3QoLWVuZHNfd2l0aCgiX3VuaXYiKSkKCnBobENlbnN1czE3IDwtIAogIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgCiAgICAgICAgICB2YXJpYWJsZXMgPSBjKCJCMDEwMDNfMDAxIiwgIkIxOTAxM18wMDEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIkIwMjAwMV8wMDIiLCAiQjI1MDc3XzAwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJCMjUwNjRfMDAxIiwgIkIyNTAzNV8wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAiQjI1MDAzXzAwMSIsICJCMjUwMDNfMDAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkIyNTAwMV8wMDEiLCAiQjI1MDAyXzAwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJCMjUwMDJfMDAzIiwgIkIyNTA4MV8wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAiQjI1MDgxXzAwNCIsICJCMTUwMDNfMDAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkIxNTAwM18wMjIiCiAgICAgICAgICAgICAgICAgICAgICAgICksIAogICAgICAgICAgeWVhciA9IDIwMTcsIAogICAgICAgICAgc3RhdGUgPSAiUEEiLCAKICAgICAgICAgIGdlb21ldHJ5ID0gRkFMU0UsIAogICAgICAgICAgY291bnR5PWMoIlBoaWxhZGVscGhpYSIpLAogICAgICAgICAgb3V0cHV0ID0gIndpZGUiKSAlPiUKICByZW5hbWUoVG90YWxfUG9wID0gIEIwMTAwM18wMDFFLAogICAgICAgICBNZWRfSW5jID0gQjE5MDEzXzAwMUUsCiAgICAgICAgIFdoaXRlX1BvcCA9IEIwMjAwMV8wMDJFLAogICAgICAgICBNZWRfVmFsdWUgPSBCMjUwNzdfMDAxRSwKICAgICAgICAgTWVkX1JlbnQgPSBCMjUwNjRfMDAxRSwKICAgICAgICAgTWVkX1N0cnVjdHVyZSA9IEIyNTAzNV8wMDFFLAogICAgICAgICBPd25lcl91bml2ID0gQjI1MDAzXzAwMUUsCiAgICAgICAgIE93bmVycyA9IEIyNTAwM18wMDJFLAogICAgICAgICBUb3RfVW5pdHMgPSBCMjUwMDFfMDAxRSwKICAgICAgICAgVmFjX3VuaXYgPSBCMjUwMDJfMDAxRSwKICAgICAgICAgVmFjYW50cyA9IEIyNTAwMl8wMDNFLAogICAgICAgICBNb3J0X3N0YXR1c191bml2ID0gQjI1MDgxXzAwMUUsCiAgICAgICAgIFNlY19Nb3J0ID0gQjI1MDgxXzAwNEUsCiAgICAgICAgIEJhY2hfdW5pdiA9IEIxNTAwM18wMDFFLAogICAgICAgICBCYWNoID0gQjE1MDAzXzAyMkUKICAgICAgICAgKSAlPiUKICBkcGx5cjo6c2VsZWN0KFRvdGFsX1BvcCwgTWVkX0luYywgV2hpdGVfUG9wLAogICAgICAgICAgICAgICAgTWVkX1ZhbHVlLCBNZWRfUmVudCwgTWVkX1N0cnVjdHVyZSwgCiAgICAgICAgICAgICAgICBPd25lcl91bml2LCBPd25lcnMsIFRvdF9Vbml0cywKICAgICAgICAgICAgICAgIFZhY191bml2LCBWYWNhbnRzLCBNb3J0X3N0YXR1c191bml2LCAKICAgICAgICAgICAgICAgIFNlY19Nb3J0LCBCYWNoX3VuaXYsIEJhY2gsIEdFT0lEKSAlPiUKICBtdXRhdGUoCiAgICBNZWRfUmVudCA9IE1lZF9SZW50ICogQ1BJLAogICAgTWVkX1ZhbHVlID0gTWVkX1ZhbHVlICogQ1BJLAogICAgUGVyY2VudF9XaGl0ZSA9IFdoaXRlX1BvcCAvIFRvdGFsX1BvcCwKICAgICAgICAgUGVyY2VudF9Pd25lciA9IE93bmVycyAvIE93bmVyX3VuaXYsCiAgICAgICAgIFBlcmNlbnRfVmFjYW50ID0gVmFjYW50cyAvIFZhY191bml2LAogICAgICAgICBQZXJjZW50XzJtb3J0ID0gU2VjX01vcnQgLyBNb3J0X3N0YXR1c191bml2LAogICAgICAgICAgICAgICAgICBQZXJjZW50X2JhY2ggPSBCYWNoIC8gQmFjaF91bml2LAogICAgICAgICBSZW50X0luY29tZV9SYXRpbyA9ICBNZWRfUmVudCAvIChNZWRfSW5jLzEyKSwKICAgICAgICAgKSAlPiUKICAjcmVwbGFjZShpcy5uYSguKSwgLjAwMDAwMDAxKSAlPiUKICAgIHNlbGVjdCgtZW5kc193aXRoKCJfdW5pdiIpKQoKIyBJbnRlcnBvbGF0ZSBiYXNlZCBvbiBwb3B1bGF0aW9uIGluIG9yZGVyIHRvIHRyYW5zbGF0ZSAyMDE3IHRyYWN0cyB0byAyMDIwIHRyYWN0cwojIHdvcmthcm91bmQgYmVjYXVzZSB0aWR5Y2Vuc3VzIHdhc24ndCBkb3dubG9hZGluZyBnZW9tZXRyeQoKI2dldCBqdXN0IHBvcHVsYXRpb24gZnJvbSBwaGxDZW5zdXMKcG9wXzIyIDwtIHBobENlbnN1czIyICU+JQogIHNlbGVjdChHRU9JRCwgVG90YWxfUG9wKQoKI2dldCB0cmFjdHM7IGpvaW4gcG9wdWxhdGlvbiB0byB0cmFjdHMKcGhsX3RyYWN0czIyIDwtIHRyYWN0cyhzdGF0ZSA9ICJQQSIsCiAgICAgICAgICAgICAgICAgICAgIGNvdW50eSA9ICJQaGlsYWRlbHBoaWEiLAogICAgICAgICAgICAgICAgICAgICB5ZWFyID0gIjIwMjIiKSAlPiUKICBsZWZ0X2pvaW4ocG9wXzIyKSAlPiUKICBzdF9zZigpCgojam9pbiB0cmFjdHMgdG8gQ2Vuc3VzIGRhdGEKcGhsQ2Vuc3VzMjIgPC0gcGhsQ2Vuc3VzMjIgJT4lCiAgbGVmdF9qb2luKHBobF90cmFjdHMyMiAlPiUKICAgICAgICAgICAgICBzZWxlY3QoR0VPSUQsIGdlb21ldHJ5KSkgJT4lCiAgc3Rfc2YoKQoKI2dldCAyMDE3IHRyYWN0cwpwaGxfdHJhY3RzMTcgPC0gdHJhY3RzKHN0YXRlID0gIlBBIiwKICAgICAgICAgICAgICAgICAgICAgY291bnR5ID0gIlBoaWxhZGVscGhpYSIsCiAgICAgICAgICAgICAgICAgICAgIHllYXIgPSAiMjAxNyIpCgojam9pbiB0cmFjdHMgdG8gMjAxNyBDZW5zdXMgZGF0YQpwaGxDZW5zdXMxNyA8LSBwaGxDZW5zdXMxNyAlPiUKICBsZWZ0X2pvaW4ocGhsX3RyYWN0czE3ICU+JQogICAgICAgICAgICAgIHNlbGVjdChHRU9JRCxnZW9tZXRyeSkpICU+JQogIHN0X3NmKCkKCnNmX3VzZV9zMihGQUxTRSkKCmNlbnN1czE3X2ludGVycG9sYXRlX3B3IDwtIGludGVycG9sYXRlX3B3KAogIHBobENlbnN1czE3LAogIHBobENlbnN1czIyLAogIHRvX2lkID0gIkdFT0lEIiwKICBleHRlbnNpdmUgPSBUUlVFLCAKICB3ZWlnaHRzID0gcGhsX3RyYWN0czIyLAogIHdlaWdodF9jb2x1bW4gPSAiVG90YWxfUG9wIiwKICBjcnMgPSA0MjY5KQoKY2Vuc3VzXzIyX2FuZF9jb21wYXJpc29uIDwtIHBobENlbnN1czIyICU+JQogIGxlZnRfam9pbihzdF9kcm9wX2dlb21ldHJ5KGNlbnN1czE3X2ludGVycG9sYXRlX3B3KSwgCiAgICAgICAgICAgIGJ5ID0gIkdFT0lEIiwKICAgICAgICAgICAgc3VmZml4ID0gYygiXzIwMjIiLCAiXzIwMTciKSkgJT4lCiAgbXV0YXRlKFRvdGFsX1BvcF9ORVQgPSBUb3RhbF9Qb3BfMjAyMiAtIFRvdGFsX1BvcF8yMDE3LCAKICAgICAgICAgTWVkX0luY19ORVQgPSBNZWRfSW5jXzIwMjIgLSBNZWRfSW5jXzIwMTcsIAogICAgICAgICBXaGl0ZV9Qb3BfTkVUID0gV2hpdGVfUG9wXzIwMjIgLSBXaGl0ZV9Qb3BfMjAxNywKICAgICAgICAgTWVkX1ZhbHVlX05FVCA9IE1lZF9WYWx1ZV8yMDIyIC0gTWVkX1ZhbHVlXzIwMTcsIAogICAgICAgICBNZWRfUmVudF9ORVQgPSBNZWRfUmVudF8yMDIyIC0gTWVkX1JlbnRfMjAxNywgCiAgICAgICAgIE93bmVyc19ORVQgPSBPd25lcnNfMjAyMiAtIE93bmVyc18yMDE3LCAKICAgICAgICAgVG90X1VuaXRzX05FVCA9IFRvdF9Vbml0c18yMDIyIC0gVG90X1VuaXRzXzIwMTcsCiAgICAgICAgIFZhY2FudHNfTkVUID0gVmFjYW50c18yMDIyIC0gVmFjYW50c18yMDE3LCAKICAgICAgICAgU2VjX01vcnRfTkVUID0gU2VjX01vcnRfMjAyMiAtIFNlY19Nb3J0XzIwMTcsCiAgICAgICAgIFBlcmNlbnRfV2hpdGVfTkVUID0gUGVyY2VudF9XaGl0ZV8yMDIyIC0gUGVyY2VudF9XaGl0ZV8yMDE3LAogICAgICAgICBQZXJjZW50X093bmVyX05FVCA9IFBlcmNlbnRfT3duZXJfMjAyMiAtIFBlcmNlbnRfT3duZXJfMjAxNywKICAgICAgICAgUGVyY2VudF9WYWNhbnRfTkVUID0gUGVyY2VudF9WYWNhbnRfMjAyMiAtIFBlcmNlbnRfVmFjYW50XzIwMTcsCiAgICAgICAgIFBlcmNlbnRfMm1vcnRfTkVUID0gUGVyY2VudF8ybW9ydF8yMDIyIC0gUGVyY2VudF8ybW9ydF8yMDE3LAogICAgICAgICBSZW50X0luY29tZV9SYXRpb19ORVQgPSBSZW50X0luY29tZV9SYXRpb18yMDIyIC0gUmVudF9JbmNvbWVfUmF0aW9fMjAxNywKICAgICAgICAgQmFjaF9ORVQgPSBCYWNoXzIwMjIgLSBCYWNoXzIwMTcsCiAgICAgICAgIFBlcmNlbnRfYmFjaF9ORVQgPSBQZXJjZW50X2JhY2hfMjAyMiAtIFBlcmNlbnRfYmFjaF8yMDE3LAogICAgICAgICAKICAgICAgICAgVG90YWxfUG9wX1BDVCA9IGlmZWxzZShUb3RhbF9Qb3BfMjAxNz4wLCAoVG90YWxfUG9wX05FVCAvIFRvdGFsX1BvcF8yMDE3KSwgTkEpKjEwMCwgCiAgICAgICAgIE1lZF9JbmNfUENUID0gaWZlbHNlKE1lZF9JbmNfMjAxNyA+MCwgKE1lZF9JbmNfTkVUIC8gTWVkX0luY18yMDE3KSwgTkEpKjEwMCwgCiAgICAgICAgIFdoaXRlX1BvcF9QQ1QgPSBpZmVsc2UoV2hpdGVfUG9wXzIwMTcgPjAsIChXaGl0ZV9Qb3BfTkVUIC8gV2hpdGVfUG9wXzIwMTcpLCBOQSkqMTAwLAogICAgICAgICBNZWRfVmFsdWVfUENUID0gaWZlbHNlKE1lZF9WYWx1ZV8yMDE3PjAsIChNZWRfVmFsdWVfTkVUIC8gTWVkX1ZhbHVlXzIwMTcpLCBOQSkqMTAwLCAKICAgICAgICAgTWVkX1JlbnRfUENUID0gaWZlbHNlKE1lZF9SZW50XzIwMTc+MCwgKE1lZF9SZW50X05FVCAvIE1lZF9SZW50XzIwMTcpLE5BKSoxMDAsIAogICAgICAgICBPd25lcnNfUENUID0gaWZlbHNlKE93bmVyc18yMDE3PjAsICggT3duZXJzX05FVCAvIE93bmVyc18yMDE3KSxOQSkqMTAwLCAKICAgICAgICAgVG90X1VuaXRzX1BDVCA9IGlmZWxzZShUb3RfVW5pdHNfMjAxNz4wLCAoVG90X1VuaXRzX05FVCAvIFRvdF9Vbml0c18yMDE3KSxOQSkqMTAwLAogICAgICAgICBWYWNhbnRzX1BDVCA9IGlmZWxzZShWYWNhbnRzXzIwMTc+MCwoVmFjYW50c19ORVQgLyBWYWNhbnRzXzIwMTcpLE5BKSoxMDAsIAogICAgICAgICBTZWNfTW9ydF9QQ1QgPSBpZmVsc2UoU2VjX01vcnRfMjAxNz4wLChTZWNfTW9ydF9ORVQgLyBTZWNfTW9ydF8yMDE3KSxOQSkqMTAwLAogICAgICAgICBQZXJjZW50X1doaXRlX1BDVCA9aWZlbHNlKFBlcmNlbnRfV2hpdGVfMjAxNz4wLChQZXJjZW50X1doaXRlX05FVCAvIFBlcmNlbnRfV2hpdGVfMjAxNyksTkEpKjEwMCwKICAgICAgICAgCiAgICAgICAgICMgaXMgaXQgYmFkIHRvIGNhbGN1bGF0ZSBwZXJjZW50IGNoYW5nZSB1c2luZyBwZXJjZW50YWdlcz8KICAgICAgICAgCiAgICAgICAgIFBlcmNlbnRfT3duZXJfUENUID0gaWZlbHNlKFBlcmNlbnRfT3duZXJfMjAxNz4wLChQZXJjZW50X093bmVyX05FVCAvIFBlcmNlbnRfT3duZXJfMjAxNyksTkEpKjEwMCwKICAgICAgICAgUGVyY2VudF9WYWNhbnRfUENUID0gaWZlbHNlKFBlcmNlbnRfVmFjYW50XzIwMTc+MCwoUGVyY2VudF9WYWNhbnRfTkVUIC8gUGVyY2VudF9WYWNhbnRfMjAxNyksTkEpKjEwMCwKICAgICAgICAgUGVyY2VudF8ybW9ydF9QQ1QgPSBpZmVsc2UoUGVyY2VudF8ybW9ydF8yMDE3PjAsKFBlcmNlbnRfMm1vcnRfTkVUIC8gUGVyY2VudF8ybW9ydF8yMDE3KSxOQSkqMTAwLAogICAgICAgICAKICAgICAgICAgUmVudF9JbmNvbWVfUmF0aW9fUENUID0gaWZlbHNlKFJlbnRfSW5jb21lX1JhdGlvXzIwMTc+MCwgKFJlbnRfSW5jb21lX1JhdGlvX05FVCAvIFJlbnRfSW5jb21lX1JhdGlvXzIwMTcpLE5BKSoxMDAsCiAgICAgICAgIAogICAgICAgICBQZXJjZW50X2JhY2hfUENUID0gaWZlbHNlKFBlcmNlbnRfYmFjaF8yMDE3PjAsIChQZXJjZW50X2JhY2hfTkVUIC8gUGVyY2VudF9iYWNoXzIwMTcpLE5BKSoxMDApICU+JQogIAogIHJlbG9jYXRlKEdFT0lEKQoKYGBgCgojIyMgR2VudHJpZmljYXRpb24gSW5kaWNhdG9yCgpXZSBkZXZlbG9wZWQgYSBnZW50cmlmaWNhdGlvbiBpbmRpY2F0b3IgbWV0aG9kb2xvZ3kgYmFzZWQgb24gW2EgRHJleGVsIHN0dWR5XShodHRwczovL2RyZXhlbC5lZHUvdWhjL3Jlc291cmNlcy9icmllZnMvTWVhc3VyZS1vZi1HZW50cmlmaWNhdGlvbi1mb3ItVXNlLWluLUxvbmdpdHVkaW5hbC1QdWJsaWMtSGVhbHRoLVN0dWRpZXMtaW4tdGhlLVVTLykuIFRoaXMgaXMganVzdCBvbmUgcG9zc2libGUgaW5kaWNhdG9yIGFuZCBjb3VsZCBiZSBpbXByb3ZlZCB1cG9uIG9yIGNoYW5nZWQgYnkgaW5jb3Jwb3JhdGluZyBhZGRpdGlvbmFsIGRlbW9ncmFwaGljIHZhcmlhYmxlcy4gCgpUaGVzZSBmb3VyIGdlbnRyaWZpY2F0aW9uIGNhdGVnb3JpZXMgd2UgdXNlIHRvIGNoYXJhY3Rlcml6ZSB0cmFjdHMgYXJlOgoKKiBJbmVsaWdpYmxlCiogTm90IGdlbnRyaWZ5aW5nCiogR2VudHJpZnlpbmcKKiBJbnRlbnNlbHkgR2VudHJpZnlpbmcKCldlIGNhbiB0aGVuIHNlZSBmb3Igd2hpY2ggY2hhcmFjdGVyaXphdGlvbnMgdGhlcmUgYXJlIGRpZmZlcmVuY2VzIGluIHBlcm1pdCB0eXBlcy4gSWYgdGhlIG1lYW4gbnVtYmVyIG9mIHBlcm1pdHMgZm9yIGNlbnN1cyB0cmFjdHMgdGhhdCBhcmUgZ2VudHJpZnlpbmcgb3IgaW50ZW5zZWx5IGdlbnRyaWZ5aW5nIGlzIGxhcmdlciB0aGFuIGZvciB0cmFjdHMgdGhhdCBhcmUgaW5lbGlnaWJsZSBvciBub3QgZ2VudHJpZnlpbmcsIHdlIGtlZXAgdGhhdCBwZXJtaXQgdHlwZS4gCgoKYGBge3IgcmVzdWx0cyA9ICdoaWRlJ30KCmluZGljYXRvciA8LSBjZW5zdXNfMjJfYW5kX2NvbXBhcmlzb24gJT4lCiAgbXV0YXRlKGluZGljYXRvciA9IAogICAgICAgICAgIGNhc2Vfd2hlbigKICAgICAgICAgICAgIAogICAgICAgICAgICAgIyB0cmFjdHMgd2l0aCA8IDUwIHBlb3BsZSBvciBpbiB0b3AgcXVhcnRpbGUgb2YgbWVkaWFuIGluY29tZSBhcmUgZXhjbHVkZWQKICAgICAgICAgICAgIAogICAgICAgICAgICAgVG90YWxfUG9wXzIwMjIgPD0gNTAgfCBNZWRfSW5jXzIwMTcgPiBxdWFudGlsZShNZWRfSW5jXzIwMTcsIC43NSwgbmEucm0gPSBUUlVFKSB+ICJJbmVsaWdpYmxlIiwKICAgICAgICAgCiAgICAgICAgICAgICAjIHRyYWN0cyB3aXRoIGJlbG93IG1lZGlhbiBpbmNyZWFzZSBpbiBwY3Qgd2l0aCBiYWNoZWxvcidzIGRlZ3JlZSwKICAgICAgICAgICAgICMgYmVsb3cgbWVkaWFuIGNoYW5nZXMgaW4gcmVudCBhbmQgaG9tZSB2YWx1ZXMgYXJlIG5vdCBnZW50cmlmeWluZwogICAgICAgICAgICAgUGVyY2VudF9iYWNoX1BDVCA8IG1lZGlhbihQZXJjZW50X2JhY2hfTkVULCBuYS5ybSA9IFRSVUUpIHwgCiAgICAgICAgICAgICAgIChNZWRfVmFsdWVfTkVUIDwgbWVkaWFuKE1lZF9WYWx1ZV9ORVQsIG5hLnJtID0gVFJVRSkgJgogICAgICAgICAgICAgICAgICBNZWRfUmVudF9ORVQgPCBtZWRpYW4oTWVkX1JlbnRfTkVULCBuYS5ybSA9IFRSVUUpKSB+ICJOb3QgZ2VudHJpZnlpbmciLAogICAgICAgICAKICAgICAgICAgICAgIyB0cmFjdHMgd2l0aCBhYm92ZS1tZWRpYW4gcGVyY2VudCBpbmNyZWFzZXMgaW4gYmFjaGVsb3IgZGVncmVlIGF0dGFpbm1lbnQKICAgICAgICAgICAgIyBhbmQgRUlUSEVSIGIvdCAuNSBhbmQgLjc1IHBlcmNlbnRpbGUgaW5jcmVhc2UgaW4gaG9tZSB2YWx1ZXMgb3IgcmVudHMgPSBnZW50cmlmeWluZwogICAgICAgICAKICAgICAgICAgUGVyY2VudF9iYWNoX1BDVCA+PSBtZWRpYW4oUGVyY2VudF9iYWNoX05FVCwgbmEucm0gPSBUUlVFKSAmICgKICAgICAgICAgICAoTWVkX1ZhbHVlX05FVCA+PSBxdWFudGlsZShNZWRfVmFsdWVfTkVULCAuNTAsIG5hLnJtID0gVFJVRSkgJgogICAgICAgICAgICAgIE1lZF9WYWx1ZV9ORVQgPCBxdWFudGlsZShNZWRfVmFsdWVfTkVULCAuNzUsIG5hLnJtID0gVFJVRSkpIHwKICAgICAgICAgICAgIChNZWRfUmVudF9ORVQgPj0gcXVhbnRpbGUoTWVkX1JlbnRfTkVULCAuNTAsIG5hLnJtID0gVFJVRSkgJiAKICAgICAgICAgICAgICAgIE1lZF9SZW50X05FVCA8IHF1YW50aWxlKE1lZF9SZW50X05FVCwgLjc1LCBuYS5ybSA9IFRSVUUpKQogICAgICAgICAgIAogICAgICAgICApIH4gIkdlbnRyaWZ5aW5nIiwKICAgICAgICAgCiAgICAgICAgICMgdHJhY3RzIHdpdGggYWJvdmUtbWVkaWFuIHBlcmNlbnQgaW5jcmVhc2VzIGluIGJhY2hlbG9yIGRlZ3JlZSBhdHRhaW5tZW50CiAgICAgICAgICAgICMgYW5kIEVJVEhFUiBpbiAuNzUgcGVyY2VudGlsZSBpbmNyZWFzZSBpbiBob21lIHZhbHVlcyBvciByZW50cyA9IGludGVuc2VseSBnZW50cmlmeWluZwogICAgICAgICAKICAgICAgICAgUGVyY2VudF9iYWNoX1BDVCA+PSBtZWRpYW4oUGVyY2VudF9iYWNoX05FVCwgbmEucm0gPSBUUlVFKSAmIAogICAgICAgICAgIChNZWRfVmFsdWVfTkVUID49IHF1YW50aWxlKE1lZF9WYWx1ZV9ORVQsLjc1LCBuYS5ybSA9IFRSVUUpIHwgCiAgICAgICAgICAgICAgTWVkX1JlbnRfTkVUID49IHF1YW50aWxlKE1lZF9SZW50X05FVCwuNzUsIG5hLnJtID0gVFJVRSkKICAgICAgICAgICApCiAgICAgICAgICAKICAgICAgICAgIH4gIkludGVuc2VseSBnZW50cmlmeWluZyIpCiAgKSAlPiUKICAKICBzZWxlY3QoR0VPSUQsIGluZGljYXRvciwgVG90YWxfUG9wXzIwMjIsIFRvdGFsX1BvcF8yMDE3LCAKICAgICAgICAgTWVkX0luY18yMDIyLCBNZWRfSW5jXzIwMTcsCiAgICAgICAgIFBlcmNlbnRfYmFjaF9ORVQsIE1lZF9WYWx1ZV9ORVQsIE1lZF9SZW50X05FVCkgCgpgYGAKCiMjIyBQZXJtaXQgU2VsZWN0aW9uCgpXZSBjYW4gc2VlIGJlbG93IGhvdyBlYWNoIHBlcm1pdCB0eXBlIHJlbGF0ZXMgdG8gZWFjaCBnZW50cmlmaWNhdGlvbiBpbmRpY2F0b3IsIGFuZCB3ZSBjYW4gYWxzbyBjYWxjdWxhdGUgdGhlIG5ldCBkaWZmZXJlbmNlIGFuZCBwZXJjZW50IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbnVtYmVyIG9mIHBlcm1pdHMgaW4gbm90IGdlbnRyaWZ5aW5nIHRyYWN0cyB2ZXJzdXMgaW50ZW5zZWx5IGdlbnRyaWZ5aW5nIHRyYWN0cy4gCgpgYGB7cn0KaW5kaWNhdG9yX3RhYmxlIDwtIGluZGljYXRvciAlPiUgCiAgICBkcGx5cjo6c2VsZWN0KC1Ub3RhbF9Qb3BfMjAyMiwgLVRvdGFsX1BvcF8yMDE3LCAKICAgICAgICAgLU1lZF9JbmNfMjAyMiwgLU1lZF9JbmNfMjAxNywKICAgICAgICAgLVBlcmNlbnRfYmFjaF9ORVQsIC1NZWRfVmFsdWVfTkVULCAtTWVkX1JlbnRfTkVUKSAlPiUKICAgIGxlZnRfam9pbih0cmFjdF9wZXJtaXRzKSAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgc2VsZWN0KC1HRU9JRCkgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCB2YWx1ZSwgLWluZGljYXRvcikgJT4lCiAgZ3JvdXBfYnkoaW5kaWNhdG9yLCBWYXJpYWJsZSkgJT4lCiAgc3VtbWFyaXNlX2lmKGlzLm51bWVyaWMsIG1lYW4sIG5hLnJtID0gVFJVRSkgJT4lCiAgdW5ncm91cCgpICU+JQogIHNwcmVhZChrZXkgPSBpbmRpY2F0b3IsIHZhbHVlID0gdmFsdWUpICU+JQogIG11dGF0ZV9pZihpcy5udW1lcmljLCByb3VuZCwgZGlnaXRzID0gMykgJT4lCiAgc2VsZWN0KFZhcmlhYmxlLCBJbmVsaWdpYmxlLCBgTm90IGdlbnRyaWZ5aW5nYCwgR2VudHJpZnlpbmcsIGBJbnRlbnNlbHkgZ2VudHJpZnlpbmdgKSAKCmtlZXAgPC0gaW5kaWNhdG9yX3RhYmxlICU+JQogIG11dGF0ZShnX3RvX25vbl9nX1BDVCA9IHJvdW5kKChHZW50cmlmeWluZyAtIGBOb3QgZ2VudHJpZnlpbmdgKSAvIGBOb3QgZ2VudHJpZnlpbmdgLCA0KSAqMTAwLAogICAgICAgICBpZ190b19ub25fZ19QQ1QgPSByb3VuZCgoYEludGVuc2VseSBnZW50cmlmeWluZ2AgLSBgTm90IGdlbnRyaWZ5aW5nYCkgLyBgTm90IGdlbnRyaWZ5aW5nYCwgNCkgKjEwMCkgJT4lCiAgZmlsdGVyKEdlbnRyaWZ5aW5nID49IDEgJiBgSW50ZW5zZWx5IGdlbnRyaWZ5aW5nYD49MSAmIGdfdG9fbm9uX2dfUENUID4gMCAmIGlnX3RvX25vbl9nX1BDVCA+IDApICU+JQogIHNlbGVjdCgtZ190b19ub25fZ19QQ1QsIC1pZ190b19ub25fZ19QQ1QpCgprZWVwICU+JQogIGRhdGF0YWJsZSgpCgprZWVwIDwtIHVuaXF1ZShrZWVwJFZhcmlhYmxlKSAKCmBgYAoKSGVyZSwgd2UgZmlsdGVyIG9ubHkgZm9yIHBlcm1pdHMgd2hlcmUgdGhlcmUgaXMgYSBub24temVybyBwZXJjZW50IGRpZmZlcmVuY2UgYmV0d2VlbiBpbnRlbnNlbHkgZ2VudHJpZnlpbmcgdHJhY3RzIGFuZCBub24tZ2VudHJpZnlpbmcgdHJhY3RzLCBhbmQga2VlcCBvbmx5IHRob3NlIHBlcm1pdHMgYW5kIGF0dGFjaCB0aGF0IHRvIGNlbnN1cyBkYXRhLCBzbyBlYWNoIHRyYWN0IGlzIGFzc2lnbmVkIGFuIGluZGljYXRvciBmb3Igd2hldGhlciBvciBub3QgaXQgaXMgZ2VudHJpZnlpbmcuIAoKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9Cgp0cmFjdF9wZXJtaXRzX2ZpbmFsIDwtIHJlc19wZXJtaXRzMiAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgbXV0YXRlKHBlcm1pdCA9IHBhc3RlKHBlcm1pdGRlc2NyaXB0aW9uLCAiLSIsIHR5cGVvZndvcmspLAogICAgICAgICB5ZWFyID0geWVhcihhcy5EYXRlKHBlcm1pdGlzc3VlZGF0ZSkpKSAlPiUKICBmaWx0ZXIoeWVhciA+PSAyMDE4ICYgcGVybWl0ICVpbiUga2VlcCkgJT4lCiAgc2VsZWN0KC1wZXJtaXRpc3N1ZWRhdGUsIC1wZXJtaXRkZXNjcmlwdGlvbiwgLXBlcm1pdHR5cGUsIC10eXBlb2Z3b3JrKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIHZhbHVlLCAtR0VPSUQsIC15ZWFyKSAlPiUKICBkcGx5cjo6Y291bnQoVmFyaWFibGUsIHZhbHVlLCBHRU9JRCwgeWVhcikgJT4lCiAgZ3JvdXBfYnkoR0VPSUQsIHllYXIpICU+JQogIG11dGF0ZShUT1RBTCA9IHN1bShuKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIHNwcmVhZChrZXkgPSB2YWx1ZSwgdmFsdWUgPSBuKSAlPiUKICBzZWxlY3QoR0VPSUQsIHllYXIsIFRPVEFMKSAlPiUKICByZXBsYWNlKGlzLm5hKC4pLCAwKSAKCmNlbnN1c19wZXJtaXRzIDwtIAogIGV4cGFuZC5ncmlkKEdFT0lEID0gdW5pcXVlKHRyYWN0X3Blcm1pdHNfZmluYWwkR0VPSUQpLAogICAgICAgICAgICAgIHllYXI9dW5pcXVlKHRyYWN0X3Blcm1pdHNfZmluYWwkeWVhcikpICU+JQogIGxlZnRfam9pbihpbmRpY2F0b3IgJT4lIHNlbGVjdChHRU9JRCwgaW5kaWNhdG9yKSkgJT4lCiAgbGVmdF9qb2luKHRyYWN0X3Blcm1pdHNfZmluYWwpICU+JQogIGxlZnRfam9pbihjZW5zdXNfMjJfYW5kX2NvbXBhcmlzb24gJT4lIHN0X2Ryb3BfZ2VvbWV0cnkoKSkgJT4lCiAgbXV0YXRlKFRPVEFMID0gaWZlbHNlKGlzLm5hKFRPVEFMKSwgMCwgVE9UQUwpKSAlPiUKICBmaWx0ZXIoVG90YWxfUG9wXzIwMjIgPiAwKSAlPiUgCiAgc2VsZWN0KC1nZW9tZXRyeSkgJT4lCiAgbGVmdF9qb2luKHBobF90cmFjdHM0MzI2KSAlPiUKICBzdF9hc19zZigpCiAgCmNlbnN1c19wZXJtaXRzJGluZGljYXRvciA8LSBmYWN0b3IoY2Vuc3VzX3Blcm1pdHMkaW5kaWNhdG9yLCBvcmRlcmVkID0gVFJVRSwgbGV2ZWxzID0gYygiSW5lbGlnaWJsZSIsICJOb3QgZ2VudHJpZnlpbmciLCAiR2VudHJpZnlpbmciLCAiSW50ZW5zZWx5IGdlbnRyaWZ5aW5nIikpCgpgYGAKCiMjIyBFeHBsb3JhdG9yeSBBbmFseXNpcyBvZiBGaWx0ZXJlZCBEZXBlbmRlbnQgVmFyaWFibGUgCgpOb3cgdGhhdCB3ZSBoYXZlIGZpbHRlcmVkIHdoYXQgd2lsbCB1bHRpbWF0ZWx5IGJlY29tZSBvdXIgZGVwZW5kZW50IHZhcmlhYmxlLCB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlICptYW55KiBjZW5zdXMgdHJhY3RzIHdoZXJlIHRoZXJlIGFyZSBubyBwZXJtaXRzIGlzc3VlZCBhY3Jvc3MgYWxsIHllYXJzLiAKCmBgYHtyfQoKZ2dwbG90KGNlbnN1c19wZXJtaXRzKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBUT1RBTCksIGJpbnMgPSA0MCwgZmlsbCA9ICIjZmZiNjAwIikgKwogIGxhYnModGl0bGUgPSAiUGVybWl0IERpc3RyaWJ1dGlvbiIsIGNhcHRpb24gPSAiRmlndXJlIDQiKQoKYGBgCgpUaGVyZSB3YXMgYSBodWdlIHNwaWtlIG9mIHBlcm1pdHMgaXNzdWVkIGluIDIwMjAgY2l0eXdpZGUsIGxpa2VseSBzcHVycmVkIGJ5IHRoZSBhbm5vdW5jZWQgW3JvbGxiYWNrIG9mIHRoZSB0YXggYWJhdGVtZW50IHBlcmlvZF0oaHR0cHM6Ly93d3cyLmxhdy50ZW1wbGUuZWR1LzEwcS9waGlsYWRlbHBoaWEtY2l0eS1jb3VuY2lsLWVuYWN0cy1zd2VlcGluZy1jaGFuZ2VzLXRvLXRoZS10YXgtYWJhdGVtZW50LXByb2dyYW0vIzp+OnRleHQ9SW4lMjBQaGlsYWRlbHBoaWElMkMlMjByZXNpZGVudGlhbCUyMHByb3BlcnRpZXMlMjBhcmUsZmlsZWQlMjBhZnRlciUyMERlY2VtYmVyJTIwMjElMkMlMjAyMDIwLikgYW5kIHRoZSBoaXN0b3JpY2FsbHkgbG93IGludGVyZXN0IHJhdGVzLiAKQXMgYSByZXN1bHQsIFsyMDIxIHdhcyBhIG1ham9yIHBlcm1pdCBib29tIHllYXJdKGh0dHBzOi8vd3d3LmlucXVpcmVyLmNvbS9yZWFsLWVzdGF0ZS9ob3VzaW5nL2hvdXNpbmctZGV2ZWxvcG1lbnQtcGVybWl0cy1hYmF0ZW1lbnQtcGhpbGFkZWxwaGlhLWRlY2xpbmUtMjAyMjEyMDcuaHRtbCkgYW5kIFtEZWNlbWJlciAyMDIxIGV4cGVyaWVuY2VkIHRoZSB0aGUgaGlnaGVzdCBjb3VudCBvZiBuZXcgY29uc3RydWN0aW9uIHBlcm1pdHMgaXNzdWVkIHNpbmNlIEphbnVhcnkgMjAxNl0oaHR0cHM6Ly93d3cuZWNvbm9teWxlYWd1ZS5vcmcvcmVzb3VyY2VzL2luc2lkZS1waGlsYWRlbHBoaWFzLWltbWluZW50LWNvbnN0cnVjdGlvbi1ib29tKS4gVGhlIFtib29tIGNvbnRpbnVlZCBpbiAyMDIyXShodHRwczovL3doeXkub3JnL2FydGljbGVzL3BoaWxseS1pcy1vbi10cmFjay10by1idWlsZC1hLXJlY29yZC1udW1iZXItb2YtYXBhcnRtZW50cy1pbi0yMDIyLWFuYWx5c3Qtc2F5cy8pIGFuZCBuZXdzIGFydGljbGVzIHRocm91Z2hvdXQgdGhlIHllYXJzIGNvbW1lbnRlZCBvbiB0aGUgWyJhYnN1cmQiIG51bWJlciBvZiBuZXcgY29uc3J1Y3Rpb24gcGVybWl0cyBpc3N1ZWRdKGh0dHBzOi8vd3d3LmJpc25vdy5jb20vcGhpbGFkZWxwaGlhL25ld3MvY29uc3RydWN0aW9uLWRldmVsb3BtZW50L3BoaWxhZGVscGhpYS1jb25zdHJ1Y3Rpb24tYm9vbS0yMDIxLXRheC1hYmF0ZW1lbnQtMTExODc0KSBpbiBQaGlsYWRlbHBoaWEuIAoKCmBgYHtyfQpjZW5zdXNfcGVybWl0cyAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpc2UoVE9UQUwgPSBzdW0oVE9UQUwpKSAlPiUKICBmaWx0ZXIoIWlzLm5hKHllYXIpKSAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgZ2dwbG90KGFlcyAoeCA9IHllYXIsIHkgPSBUT1RBTCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICIjZmZiNjAwIikgKwogIHBsb3RUaGVtZSgpICsKICBsYWJzKHRpdGxlID0gIlBlcm1pdHMgYnkgWWVhciIsIGNhcHRpb24gPSAiRmlndXJlIDUiKQpgYGAKCk5hdGlvbmFsIGFuZCBjaXR5LXdpZGUgcG9saWNpZXMgbWF5IGJlIGluZmx1ZW5jaW5nIGRldmVsb3BtZW50IG9uIGEgY2l0eSBzY2FsZS4gR2VudHJpZmljYXRpb24gaW5kaWNhdG9ycyBwcm92aWRlIGEgY2x1ZSBhcyB0byB3aGljaCB0cmFjdHMgYXJlIGV4cGVyaWVuY2luZyBjaGFuZ2UsIG9yIHZlcnkgcmFwaWQgY2hhbmdlLCBpZiBhdCBhbGwuIEJ1dCB3ZSBoYXZlbid0IHlldCBob25lZCBpbiBvbiBuZWlnaGJvcmhvb2RzIHRvIGd1aWRlIG91ciBmZWF0dXJlIGVuZ2luZWVyaW5nLiBXZSBsb29rZWQgYXQgcGVybWl0cyBieSBuZWlnaGJvcmhvb2QgZm9yIHNvbWUgY2x1ZXMgYW5kIGNvdWxkIGltbWVkaWF0ZWx5IHNlZSB0aGF0IHRoZXJlIHdlcmUgaGlnaCBwZXJtaXRzIGlzc3VlZCBmb3IgbmVpZ2hib3Job29kcyB0aGF0IGhhdmUgYmVlbiB0aGUgc3ViamVjdCBvZiBsb2NsYSBhcnRpY2xlcyBhYm91dCBnZW50cmlmaWNhdGlvbiwgaW5jbHVkaW5nIFtGaXNodG93bl0oaHR0cHM6Ly93d3cuZm9yYmVzLmNvbS9zaXRlcy9wZXRlcnRheWxvci8yMDE4LzA1LzAyL2hvdy1maXNodG93bi1waGlsYWRlbHBoaWEtYmVjYW1lLWFtZXJpY2FzLWhvdHRlc3QtbmV3LW5laWdoYm9yaG9vZC8/c2g9MTQ5NjVkODczMmU1KSwgW1BvaW50IEJyZWV6ZV0oaHR0cHM6Ly93d3cuaW5xdWlyZXIuY29tL25ld3MvcG9pbnQtYnJlZXplLXRpb2dhLW5pY2V0b3duLWdlbnRyaWZpY2F0aW9uLXBoaWxhZGVscGhpYS1jZW5zdXMtbmVpZ2hib3Job29kLWNoYW5nZS0yMDIwMDEwOS5odG1sKSwgYW5kIFtMb3dlciBLZW5zaW5ndG9uXShodHRwczovL3d3dy5pbnF1aXJlci5jb20vcmVhbC1lc3RhdGUvaW5nYS1zYWZmcm9uL2tlbnNpbmd0b24tZ2VudHJpZmljYXRpb24tc2hpZnQtY2FwaXRhbC1kcnVncy1vcGlvaWRzLWltcGFjdC1zZXJ2aWNlcy1uZWlnaGJvcmhvb2QtZGV2ZWxvcG1lbnQtaG91c2luZy1tYWtlbi1mZWxzLWZ1bmQtaW5nYS1zYWZmcm9uLTIwMjEwMjEwLmh0bWwpCgpgYGB7cn0KcmVzX3Blcm1pdHMzIDwtIHJlc19wZXJtaXRzICU+JQogIGdyb3VwX2J5KHBlcm1pdGRlc2NyaXB0aW9uLCB0eXBlb2Z3b3JrKSAlPiUKICBtdXRhdGUobiA9IG4oKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIHNlbGVjdChwZXJtaXR0eXBlLCBwZXJtaXRkZXNjcmlwdGlvbiwgdHlwZW9md29yaywgZ2VvbWV0cnksIHBlcm1pdGlzc3VlZGF0ZSkgJT4lCiAgc3Rfam9pbihwaGwubmgpICU+JQogICAgbXV0YXRlKHBlcm1pdCA9IHBhc3RlKHBlcm1pdGRlc2NyaXB0aW9uLCAiLSIsIHR5cGVvZndvcmspLAogICAgICAgICB5ZWFyID0geWVhcihhcy5EYXRlKHBlcm1pdGlzc3VlZGF0ZSkpKSAlPiUKICBmaWx0ZXIoeWVhcj49MjAxOCAmIHBlcm1pdCAlaW4lIGtlZXApCgpyZXNfcGVybWl0czMgPC0gcmVzX3Blcm1pdHMzICU+JQogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUKICBncm91cF9ieShtYXBuYW1lLCB5ZWFyKSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKG4pKQoKcmVzX3Blcm1pdHMzICU+JQogIGRhdGF0YWJsZSgpCmBgYAoKV2hlbiB3ZSBsb29rIGF0IHRoaXMgb3ZlciB0aW1lLCB3ZSBjYW4gc2VlIGNsZWFybHkgdGhhdCB0aGUgbmVpZ2hib3Job29kIHBhdHRlcm5zIHJlbWFpbiB0aGUgc2FtZSwgd2l0aCB0aGUgc2FtZSBuZWlnaGJvcmhvb2RzIHJlY2VpdmluZyBwZXJtaXRzIGFjcm9zcyBhIDYteWVhciBwZXJpb2QuIAoKYGBge3J9CgpyZXNfcGVybWl0czMgPC0gcmVzX3Blcm1pdHMzICU+JQogIGxlZnRfam9pbihwaGwubmgpCgpyZXNfcGVybWl0czMgJT4lCiAgdW5ncm91cCgpICU+JQogIHN0X2FzX3NmKCkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoYWVzKGZpbGwgPSBjdXQobiwgYnJlYWtzID0gNykpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTEpICsKICBmYWNldF93cmFwKH55ZWFyKSArCiAgbWFwVGhlbWUoKSArCiAgbGFicyh0aXRsZSA9ICJQZXJtaXRzIGJ5IFllYXIsIFBoaWxhZGVscGhpYSIsIGNhcHRpb24gPSAiRmlndXJlIDYiKQoKCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSBleHBsb3JlZCBnZW50cmlmaWNhdGlvbiBpbmRpY2F0b3JzIGFjcm9zcyBQaGlsYWRlbHBoaWEgbmVpZ2hib3Job29kcyBhbmQgdHJhY3RzLCBwZXJtaXRzIGlzc3VlZCBvdmVyIHRpbWUgYW5kIHNwYWNlLCBhbmQgc29tZSBiYXNpYyBkZW1vZ3JhcGhpYyBjaGFyYWN0ZXJpc3RpY3MsIHdlIGRldmVsb3BlZCBhIGNvbXBvc2l0ZSBtYXAgd2hlcmUgd2UgY2FuIHNlZSB3aGljaCBuZWlnaGJvcmhvb2RzIGhhdmUgZ2VudHJpZmljYXRpb24gaW5kaWNhdG9ycyBhbmQgYXJlIHNlZWluZyBhIGxvdCBvZiBuZXcgZGV2ZWxvcG1lbnQgZm9yIGEgZ2l2ZW4geWVhci4gKkNsaWNrIHRocm91Z2ggdGhlIHllYXJzIHRvIGxvb2sgYXQgaG93IGdlbnRyaWZpY2F0aW9uIGluZGljYXRvcnMgYW5kIHBlcm1pdHMgY2hhbmdlIG92ZXIgdGltZS4qCgpJbiAyMDIxLCB0aGVyZSBhcmUgYXJlYSB0aGF0IGFyZSBnZW50cmlmeWluZyBidXQgbm90IHNlZWluZyBhIGxvdCBvZiBuZXcgZGV2ZWxvcG1lbnQgLSBwYXJ0aWN1bGFybHkgaW4gdGhlIE5vcnRoZWFzdCBhbmQgTm9ydGh3ZXN0LiBQcmVkaWN0aW5nIGRldmVsb3BtZW50IGlzIHRoZXJlZm9yZSBnb2luZyB0byBiZSBzbGlnaHRseSBkaWZmZXJlbnQgdGhhbiBwcmVkaWN0aW5nIGNoYW5nZXMgaW4gcmVudHMsIGhvbWUgdmFsdWVzLCBhbmQgb3RoZXIgZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWNzIGxpa2UgcmFjaWFsIG1ha2V1cC4gCgpgYGB7cn0KYmlucyA9IGMoMCw1LDMwLDUwLDEwMCwyMDAsMzAwLEluZikKCnBlcm1pdHMxOCA8LSBjb2xvckJpbigKICBwYWxldHRlID0gInZpcmlkaXMiLAogIGJpbnMgPSBiaW5zLAogIGRvbWFpbiA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDE4KSAlPiUgLiRUT1RBTCkKCnBlcm1pdHMxOSA8LSBjb2xvckJpbigKICBwYWxldHRlID0gInZpcmlkaXMiLAogIGJpbnMgPSBiaW5zLAogIGRvbWFpbiA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDE5KSAlPiUgLiRUT1RBTCkKCnBlcm1pdHMyMCA8LSBjb2xvckJpbigKICBwYWxldHRlID0gInZpcmlkaXMiLAogIGJpbnMgPSBiaW5zLAogIGRvbWFpbiA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIwKSAlPiUgLiRUT1RBTCkKCnBlcm1pdHMyMSA8LSBjb2xvckJpbigKICBwYWxldHRlID0gInZpcmlkaXMiLAogIGJpbnMgPSBiaW5zLAogIGRvbWFpbiA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIxKSAlPiUgLiRUT1RBTCkKCnBlcm1pdHMyMiA8LSBjb2xvckJpbigKICBwYWxldHRlID0gInZpcmlkaXMiLAogIGJpbnMgPSBiaW5zLAogIGRvbWFpbiA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIyKSAlPiUgLiRUT1RBTCkKCnBlcm1pdHMyMyA8LSBjb2xvckJpbigKICBwYWxldHRlID0gInZpcmlkaXMiLAogIGJpbnMgPSBiaW5zLAogIGRvbWFpbiA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIzKSAlPiUgLiRUT1RBTCkKCmluZGljYXRvcl9wYWwgPC0gY29sb3JGYWN0b3IocGFsZXR0ZSA9IGMoIiNjMTkxZDkiLCAiIzZkNTQ4MCIsIiNmZjY2ZjAiLCAiI2ZjY2QzMiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgZG9tYWluID0gY2Vuc3VzX3Blcm1pdHMkaW5kaWNhdG9yKQoKcGVybWl0c19tYXAgPC0gbGVhZmxldCgpICU+JQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuUG9zaXRyb24pICU+JQogIGFkZFBvbHlnb25zKGRhdGEgPSBjZW5zdXNfcGVybWl0cyAlPiUgZmlsdGVyKHllYXIgPT0gMjAyMyksCiAgICAgICAgICAgICAgc3Ryb2tlID0gVFJVRSwKICAgICAgICAgICAgICBjb2xvciA9IH5pbmRpY2F0b3JfcGFsKGluZGljYXRvciksCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMiwKICAgICAgICAgICAgICBvcGFjaXR5ID0gMSwKICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IC41LAogICAgICAgICAgICAgIGZpbGxDb2xvciA9IH5wZXJtaXRzMjMoVE9UQUwpLAogICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlRvdGFsIFBlcm1pdHM6IiwgY2Vuc3VzX3Blcm1pdHMgJT4lIGZpbHRlcih5ZWFyID09IDIwMjMpICU+JSAuJFRPVEFMKSwKICAgICAgICAgICAgICBncm91cCA9ICIyMDIzIikgJT4lCiAgICAgIGFkZFBvbHlnb25zKGRhdGEgPSBjZW5zdXNfcGVybWl0cyAlPiUgZmlsdGVyKHllYXIgPT0gMjAyMiksCiAgICAgICAgICAgICAgc3Ryb2tlID0gVFJVRSwKICAgICAgICAgICAgICBjb2xvciA9IH5pbmRpY2F0b3JfcGFsKGluZGljYXRvciksCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMiwKICAgICAgICAgICAgICBvcGFjaXR5ID0gMSwKICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IC41LAogICAgICAgICAgICAgIGZpbGxDb2xvciA9IH5wZXJtaXRzMjIoVE9UQUwpLAogICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlRvdGFsIFBlcm1pdHM6IiwgY2Vuc3VzX3Blcm1pdHMgJT4lIGZpbHRlcih5ZWFyID09IDIwMjIpICU+JSAuJFRPVEFMKSwKICAgICAgICAgICAgICBncm91cCA9ICIyMDIyIikgJT4lCiAgYWRkUG9seWdvbnMoZGF0YSA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIxKSwKICAgICAgICAgICAgICBzdHJva2UgPSBUUlVFLAogICAgICAgICAgICAgIGNvbG9yID0gfmluZGljYXRvcl9wYWwoaW5kaWNhdG9yKSwKICAgICAgICAgICAgICB3ZWlnaHQgPSAyLAogICAgICAgICAgICAgIG9wYWNpdHkgPSAxLAogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gLjUsCiAgICAgICAgICAgICAgZmlsbENvbG9yID0gfnBlcm1pdHMyMShUT1RBTCksCiAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiVG90YWwgUGVybWl0czoiLCBjZW5zdXNfcGVybWl0cyAlPiUgZmlsdGVyKHllYXIgPT0gMjAyMSkgJT4lIC4kVE9UQUwpLAogICAgICAgICAgICAgIGdyb3VwID0gIjIwMjEiKSAlPiUKICBhZGRQb2x5Z29ucyhkYXRhID0gY2Vuc3VzX3Blcm1pdHMgJT4lIGZpbHRlcih5ZWFyID09IDIwMjApLAogICAgICAgICAgICAgIHN0cm9rZSA9IFRSVUUsCiAgICAgICAgICAgICAgY29sb3IgPSB+aW5kaWNhdG9yX3BhbChpbmRpY2F0b3IpLAogICAgICAgICAgICAgIHdlaWdodCA9IDIsCiAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsCiAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAuNSwKICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+cGVybWl0czIwKFRPVEFMKSwKICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJUb3RhbCBQZXJtaXRzOiIsIGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIwKSAlPiUgLiRUT1RBTCksCiAgICAgICAgICAgICAgZ3JvdXAgPSAiMjAyMCIpICU+JQogICAgYWRkUG9seWdvbnMoZGF0YSA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDE5KSwKICAgICAgICAgICAgICBzdHJva2UgPSBUUlVFLAogICAgICAgICAgICAgIGNvbG9yID0gfmluZGljYXRvcl9wYWwoaW5kaWNhdG9yKSwKICAgICAgICAgICAgICB3ZWlnaHQgPSAyLAogICAgICAgICAgICAgIG9wYWNpdHkgPSAxLAogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gLjUsCiAgICAgICAgICAgICAgZmlsbENvbG9yID0gfnBlcm1pdHMxOShUT1RBTCksCiAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiVG90YWwgUGVybWl0czoiLCBjZW5zdXNfcGVybWl0cyAlPiUgZmlsdGVyKHllYXIgPT0gMjAxOSkgJT4lIC4kVE9UQUwpLAogICAgICAgICAgICAgIGdyb3VwID0gIjIwMTkiKSAlPiUKICBhZGRQb2x5Z29ucyhkYXRhID0gY2Vuc3VzX3Blcm1pdHMgJT4lIGZpbHRlcih5ZWFyID09IDIwMTgpLAogICAgICAgICAgICAgIHN0cm9rZSA9IFRSVUUsCiAgICAgICAgICAgICAgY29sb3IgPSB+aW5kaWNhdG9yX3BhbChpbmRpY2F0b3IpLAogICAgICAgICAgICAgIHdlaWdodCA9IDIsCiAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsCiAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAuNSwKICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+cGVybWl0czE4KFRPVEFMKSwKICAgICAgICAgICAgICBncm91cCA9ICIyMDE4IiwKICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJUb3RhbCBQZXJtaXRzOiIsIGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDE4KSAlPiUgLiRUT1RBTCkpICU+JQogIGFkZExlZ2VuZChkYXRhID0gY2Vuc3VzX3Blcm1pdHMsCiAgICBwYWwgPSBpbmRpY2F0b3JfcGFsLAogICAgICAgICAgICB2YWx1ZSA9IH5pbmRpY2F0b3IsCiAgICB0aXRsZSA9ICJHZW50cmlmaWNhdGlvbiBJbmRpY2F0b3IiLAogICAgcG9zaXRpb24gPSAiYm90dG9tbGVmdCIpICU+JQogICBhZGRMYXllcnNDb250cm9sKAogICAgYmFzZUdyb3VwcyA9IGMoIjIwMjMiLCAiMjAyMiIsICIyMDIxIiwgIjIwMjAiLCAiMjAxOSIsICIyMDE4IiksCiAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpKSAKCnBlcm1pdHNfbWFwICU+JQogIHNldFZpZXcoIi03NS4xMzk0NDYiLCIzOS45OTY4NDkiLHpvb20gPSAxMSkKCmBgYAoKV2hlbiB3ZSBqdXN0IGxvb2sgYXQgb3VyIGdlbnRyaWZpY2F0aW9uIGluZGljYXRvciBhbG9uZSAod2l0aG91dCBwZXJtaXRzKSwgd2UgY2FuIHNlZSB0aGF0IG5vdCBhbGwgZGV2ZWxvcG1lbnQgaXMgYSByZXN1bHQgb2YgZ2VudHJpZmljYXRpb24sIGJ1dCB0aGVyZSBhcmUgc3RpbGwgc29tZSBhcmVhcyB3aGVyZSB0aGUgdHdvIG92ZXJsYXAuIEFsdGhvdWdoIHRoaXMgcHJvamVjdCBhaW1zIHRvIHByb3ZpZGUgdGFyZ2V0ZWQgYXJlYXMgZm9yIEhvdXNpbmcgVHJ1c3QgRnVuZHMsIHdlIGhhdmUgdG8gcmVjb2duaXplIHRoYXQgcGVybWl0cyBhbG9uZSBkbyBub3QgcGVyZmVjdGx5IGFsaWduIHdpdGggZ2VudHJpZmljYXRpb24sIGJ1dCBtZXJlbHkgcmVmbGVjdCBuZXcgZGV2ZWxvcG1lbnQuCgpgYGB7cn0KY2Vuc3VzX3Blcm1pdHMgJT4lCiAgZmlsdGVyKCFpcy5uYSh5ZWFyKSkgJT4lCiAgc3Rfc2YoKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX3NmKGFlcyhmaWxsID0gaW5kaWNhdG9yKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUxKSArCiAgbWFwVGhlbWUoKSArCiAgbGFicyh0aXRsZSA9ICJHZW50cmlmaWNhdGlvbiBJbmRpY2F0b3JzIiwgY2FwdGlvbiA9ICJGaWd1cmUgNyIpCmBgYAoKIyMgKipGRUFUVVJFIEVOR0lORUVSSU5HKioKCiMjIyBTcGF0aWFsIGFuZCBzcGF0aWFsIHRpbWUgbGFncywgQ2Vuc3VzIGRhdGEKCkRldmVsb3Bpbmcgc3BhdGlhbCBhbmQgc3BhdGlhbCB0aW1lIGxhZ3MgY2FuIGhlbHAgdXMgY2FwdHVyZSB0aGUgc3BhdGlhbCBlbmRvZ2VuZWl0eSBvZiBnZW50cmlmaWNhdGlvbiBhcyBkaXNjdXNzZWQgW2hlcmVdKGh0dHBzOi8vdXJiYW5zcGF0aWFsYW5hbHlzaXMuY29tL3BvcnRmb2xpby9wcmVkaWN0aW5nLWdlbnRyaWZpY2F0aW9uLXVzaW5nLWxvbmdpdHVkaW5hbC1jZW5zdXMtZGF0YS8jOn46dGV4dD1FbmRvZ2Vub3VzJTIwZ2VudHJpZmljYXRpb24mdGV4dD1CZWNhdXNlJTIwYXJlYXMlMjBpbiUyMGNsb3NlJTIwcHJveGltaXR5LGJ1dCUyMHdpdGglMjBsb3dlciUyMGhvdXNpbmclMjBjb3N0cykuIFdlIGNyZWF0ZWQgYSBzcGF0aWFsIHdlaWdodHMgbWF0cml4IGJhc2VkIG9uIGNvbnRpZ3VpdHkgcmVsYXRpb25zaGlwcyBvZiB0aGUgY2Vuc3VzIHRyYWN0cyBzaGFyaW5nIGVkZ2VzIGFuZCB2ZXJ0aWNlcy4gU3BlY2lmaWNhbGx5LCB3ZSBsb29rZWQgYXQgdGhlIG5lYXJlc3QgbmVpZ2hib3IgZm9yIHByaWNlcywgcmVudHMsIGFuZCBkZW1vZ3JhcGhpY3Mgb2YgbmVpZ2hib3JpbmcgY2Vuc3VzIHRyYWN0cyBhcyBwb3NzaWJsZSBrZXkgdmFyaWFibGVzIHRvIHByZWRpY3QgZm9yIHBlcm1pdCBkZXZlbG9wbWVudC4gCgpXZSBhbHNvIGNyZWF0ZWQgYSBsYWcgZm9yIHBlcm1pdHMgaXNzdWVkIHRvIGxhc3QgeWVhcidzIG5laWdoYm9ycy4gU3BhdGlhbCBsYWcgYW5kIHNwYXRpYWwgdGltZSBsYWcgaGFzIG1ham9yIHByZWRpY3RpdmUgcG90ZW50aWFsIGZvciBvdXIgbW9kZWxpbmcgZWZmb3J0cy4gVGhlIGF2ZXJhZ2UgcGVybWl0IGNvdW50IGZvciB0aGUgbmVhcmVzdCBjZW5zdXMgdHJhY3QgY2FuIGJlIGhlbHBmdWwsIGJ1dCB1dGlsaXppbmcgdGhlIG5lYXJlc3QgbmVpZ2hib3IgZm9yIHNvY2lhbCBkZW1vZ3JhcGhpYyBkYXRhIGhhcyB0aGUgcG90ZW50aWFsIHRvIGJlIG1vcmUgaW5mbHVlbmNlLiBBcyBoaWdoLWRlbWFuZCBuZWlnaGJvcmhvb2RzIGJlY29tZSBtb3JlIHByaWNlIHVuYXR0YWluYWJsZSwgcmVzaWRlbnRzIHByaWNlZCBvdXQgdHJ5IHRvIHJlbWFpbiBjbG9zZSBhbmQgY2hvb3NlIG5laWdoYm9yaG9vZHMgd2l0aCBzb21lIGFkamFjZW50IGNoYXJhY3RlcmlzdGljcyBzdWNoIGFzIGJhY2hlbG9ycyBkZWdlcmVlcywgaW5jb21lLCBvciB3aGl0ZSByZXNpZGVudHMsIGFuZCB0aGVyZWZvcmUgbW9yZSBsaWtlbGlob29kIG9mIGhpZ2hlciBzb2NpYWwgaW50ZWdyYXRpb24uIAoKYGBge3J9Cm5iIDwtIHN0X2NvbnRpZ3VpdHkoY2Vuc3VzX3Blcm1pdHMkZ2VvbWV0cnksIHF1ZWVuID0gVFJVRSkKd3QgPC0gc3Rfd2VpZ2h0cyhuYikKCmNlbnN1c19wZXJtaXRzIDwtIGNlbnN1c19wZXJtaXRzICU+JQogIG11dGF0ZShubl9tZWFuX3JlbnRfMjIgPSBzdF9sYWcoTWVkX1JlbnRfMjAyMixuYix3dCwgbmFfb2sgPSBUUlVFKSwKICAgICAgbm5fbWVhbl9wcmljZV8yMiA9IHN0X2xhZyhNZWRfVmFsdWVfMjAyMiwgbmIsd3QsIG5hX29rID0gVFJVRSksCiAgICAgIG5uX21lYW5fcmVudF8xNyA9IHN0X2xhZyhNZWRfUmVudF8yMDE3LG5iLHd0LCBuYV9vayA9IFRSVUUpLAogICAgICBubl9tZWFuX3ByaWNlXzE3ID0gc3RfbGFnKE1lZF9WYWx1ZV8yMDE3LCBuYix3dCwgbmFfb2sgPSBUUlVFKSwKICAgICAgbm5fYmFjaF8yMiA9IHN0X2xhZyhQZXJjZW50X2JhY2hfMjAyMiwgbmIsd3QsIG5hX29rID0gVFJVRSksCiAgICAgIG5uX2JhY2hfMTcgPSBzdF9sYWcoUGVyY2VudF9iYWNoXzIwMTcsIG5iLHd0LCBuYV9vayA9IFRSVUUpLAogICAgICBubl9pbmNvbWVfMjIgPSBzdF9sYWcoTWVkX0luY18yMDIyLCBuYix3dCwgbmFfb2sgPSBUUlVFKSwKICAgICAgbm5faW5jb21lXzE3ID0gc3RfbGFnKE1lZF9JbmNfMjAxNywgbmIsd3QsIG5hX29rID0gVFJVRSksCiAgICAgIG5uX3BjdF93aGl0ZV8yMiA9IHN0X2xhZyhQZXJjZW50X1doaXRlXzIwMjIsIG5iLHd0LCBuYV9vayA9IFRSVUUpLAogICAgICBubl9wY3Rfd2hpdGVfMTcgPSBzdF9sYWcoUGVyY2VudF9XaGl0ZV8yMDE3LCBuYix3dCwgbmFfb2sgPSBUUlVFKQogICAgICApCgpjZW5zdXNfcGVybWl0cyA8LSBjZW5zdXNfcGVybWl0cyAlPiUKICBncm91cF9ieShHRU9JRCkgJT4lCiAgYXJyYW5nZShHRU9JRCwgeWVhcikgJT4lCiAgbXV0YXRlKGxhc3RfeWVhcl9UT1RBTCA9IGRwbHlyOjpsYWcoVE9UQUwpLAogICAgICAgICBsYXN0X3llYXJfVE9UQUwgPSBpZmVsc2UoaXMubmEobGFzdF95ZWFyX1RPVEFMKSwgVE9UQUwsIGxhc3RfeWVhcl9UT1RBTCkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUobm5fTFlfcGVybWl0cyA9IHN0X2xhZyhsYXN0X3llYXJfVE9UQUwsIG5iLCB3dCkpCgpgYGAKCiMjIyBMb2NhbCBNb3JhbidzIEkKCk1vcmFuJ3MgSSBjYW4gaGVscCBpZGVudGlmeSBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiwgYW5kIHNpbWlsYXJpdHkgYW1vbmcgY2x1c3RlcnMgb2YgdmFsdWVzLiBNb3JhbidzIEkgdmFsdWVzIHRocm91Z2hvdXQgUGhpbGFkZWxwaGlhIGFyZSBsYXJnZWx5IGFib3ZlIDEsIGluZGljYXRpbmcgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBjbHVzdGVycyB3aGVyZWluIG5lYXJlc3QgbmVpZ2hib3JzIGFyZSBoYXZpbmcgYW4gaW1wYWN0IG9uIHBlcm1pdCBjb3VudC4gQmVsb3csIHdlIGNhbiBzZWUgdGhhdCB0aGF0IG1vc3Qgb2YgUGhpbGFkZWxwaGlhIGhhcyB2ZXJ5IGxvdyBNb3JhbidzIEkgdmFsdWVzIGV4Y2VwdCBmb3IgRmlzaHRvd24gYW5kIFVuaXZlcnNpdHkgQ2l0eSwgd2hpY2ggYXJlIG91dGxpZXJzLiBTaW1pbGFybHksIHRoZXJlIGFyZSBoaWdoZXIgcHJvYmFiaWxpdHkgdmFsdWVzIGluIHRoZSBhcmVhcyBzdXJyb3VuZGluZyBGaXNodG93biBhbmQgVW5pdmVyc2l0eSBDaXR5LCBwYXJ0aWN1bGFybHkgaW4gV2VzdCBQaGlsbHkuIAoKSGlnaGVyIHByb2JhYmlsaXR5IGZvciB0aGUgbmVhcmVzdCBuZWlnaGJvcmhvb2RzIHN1cnJvdW5kaW5nIHRoZSBoaWdoIGRlbWFuZCBuZWlnaGJvcmhvb2RzIHN1Z2dlc3QgdGhhdCB0aGVyZSBpcyBzb21lIHVuY2VydGFpbnR5IHJlZ2FyZGluZyBwcmVkaWN0aW9uIGZvciB0aGVzZSB0cmFjdHMuIEFsdGhvdWdoIHRoaXMgZ2VuZXJhbGx5IGluZGljYXRlcyB0aGF0IG91ciBjb25maWRlbmNlIGZvciBwcmVkaWN0aW5nIHRoZXNlIHRyYWN0cyBpcyBsb3dlciwgdGhlIGNsdXN0ZXJpbmcgb2YgdGhlc2UgcHJvYmFiaWxpdHkgdmFsdWVzIHN1Z2dlc3RzIHRoYXQgdGhlIG5lYXJlc3QgbmVpZ2hib3IgYW5kIHNwYXRpYWwgbGFnIGlzIGluZGljYXRpdmUgb2Ygc29tZSBmYWN0b3IsIGV2ZW4gaWYgd2UgYXJlIG5vdCBpZGVudGlmeWluZyBpdCB3aXRoIGV4YWN0bmVzcy4KCmBgYHtyfQoKbmIgPC0gcG9seTJuYihjZW5zdXNfcGVybWl0cyRnZW9tZXRyeSwgcXVlZW49VFJVRSkKd3QgPC0gbmIybGlzdHcobmIsIHN0eWxlPSJXIiwgemVyby5wb2xpY3k9VFJVRSkKCmxvY2FsX21vcmFucyA8LSBsb2NhbG1vcmFuKGNlbnN1c19wZXJtaXRzJFRPVEFMLCB3dCwgemVyby5wb2xpY3k9VFJVRSkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHJlbmFtZShjKCJMb2NhbF9Nb3JhbnNfSSIgPSAiSWkiLCAiUF9WYWx1ZSIgPSAiUHIoeiAhPSBFKElpKSkiKSkKCmNlbnN1c19wZXJtaXRzIDwtIAogIGNiaW5kKGNlbnN1c19wZXJtaXRzLCBsb2NhbF9tb3JhbnMgJT4lIHNlbGVjdChMb2NhbF9Nb3JhbnNfSSwgUF9WYWx1ZSkpICU+JSAKICBzdF9zZigpICU+JQogIG11dGF0ZShTaWduaWZpY2FudF9Ib3RzcG90cyA9IGlmZWxzZShQX1ZhbHVlIDw9IDAuMDAxLCAxLCAwKSkgCgojUGxvdCBNb3JhbnMgSQoKbG9jYWxNb3JhbnMgPC0gCiAgY2JpbmQobG9jYWxfbW9yYW5zLCBjZW5zdXNfcGVybWl0cykgJT4lIAogIHN0X3NmKCkgJT4lCiAgZHBseXI6OnNlbGVjdChUT1RBTCwKICAgICAgICAgICAgICAgIExvY2FsX01vcmFuc19JLAogICAgICAgICAgICAgICAgUF9WYWx1ZSkgJT4lCiAgbXV0YXRlKFNpZ25pZmljYW50X0hvdHNwb3RzID0gaWZlbHNlKFBfVmFsdWUgPD0gMC4wMDEsIDEsIDApKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtZ2VvbWV0cnkpCgojIyBUaGlzIGlzIGp1c3QgZm9yIHBsb3R0aW5nCnZhcnMgPC0gdW5pcXVlKGxvY2FsTW9yYW5zJFZhcmlhYmxlKQp2YXJMaXN0IDwtIGxpc3QoKQoKZm9yKGkgaW4gdmFycyl7CiAgdmFyTGlzdFtbaV1dIDwtIAogICAgZ2dwbG90KCkgKwogICAgICBnZW9tX3NmKGRhdGEgPSBmaWx0ZXIobG9jYWxNb3JhbnMsIFZhcmlhYmxlID09IGkpLCAKICAgICAgICAgICAgICBhZXMoZmlsbCA9IFZhbHVlKSwgY29sb3VyPU5BKSArCiAgICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19iKG5hbWU9IiIpICsKICAgICAgbGFicyh0aXRsZT1pKSArCiAgICAgIG1hcFRoZW1lKHRpdGxlX3NpemUgPSAxNCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpCn0KCgpkby5jYWxsKGdyaWQuYXJyYW5nZSxjKHZhckxpc3QsIG5jb2wgPSA0LCB0b3AgPSAiTG9jYWwgTW9yYW5zIEkgc3RhdGlzdGljcywgVG90YWwgUGVybWl0cyIpKQogIApgYGAKCiMjIyAqKkFkZGl0aW9uYWwgRmVhdHVyZSBFbmdpbmVlcmluZyoqCgojIyMjIENvZmZlZSBzaG9wcwoKRmluYWxseSwgd2UgZ2VuZXJhdGVkIGEgZmV3IGFtZW5pdHkgZmVhdHVyZXMgc3VjaCBhcyBzdG9yZXMgb3IgaW5mcmFzdHJ1Y3R1cmUgdGhhdCBtYXkgbWFrZSBhIG5laWdoYm9yaG9vZCBtb3JlIGF0dHJhY3RpdmUuIFRoZXNlIGluY2x1ZGUgY2FmZXMgZnJvbSAyMDE4IHRvIDIwMjMgc2luY2UgdGhlIHByZXNlbmNlIG9mIG5ldyBjYWZlcyBtYXkgYmUgY29ycmVsYXRlZCB3aXRoIGdlbnRyaWZpY2F0aW9uIG9yIG5ldyBkZXZlbG9wbWVudC4gCgpgYGB7ciByZXN1bHRzID0gJ2hpZGUnfQojY29mZmVlIHNob3BzIGJ5IHllYXIsIHRoZW4gam9pbiB0byBjZW5zdXNfcGVybWl0cy4KCiAjIGNhZmVzMTggPC0gZ2V0YmIoJ1BoaWxhZGVscGhpYSwgUEEnKSAlPiUKICMgICBvcHEoZGF0ZXRpbWUgPSAnMjAxOC0wMS0wMVQwMDowMDowMVonKSAlPiUKICMgICBhZGRfb3NtX2ZlYXR1cmUoa2V5ID0gImFtZW5pdHkiLCB2YWx1ZSA9ICJjYWZlIikgJT4lCiAjICAgb3NtZGF0YV9zZigpCiAjIAogIyBjYWZlczE4LnNmIDwtIHN0X2FzX3NmKGNhZmVzMTgkb3NtX3BvaW50cykKICMgCiAjIGNhZmVzMTguc2YgPC0gY2FmZXMxOC5zZiAlPiUKICMgICBzdF90cmFuc2Zvcm0oY3JzID0gc3RfY3JzKGNlbnN1c19wZXJtaXRzKSkgJT4lCiAjICAgc3Rfam9pbihjZW5zdXNfcGVybWl0cywgam9pbiA9IHN0X3dpdGhpbikKICMgCiAjIGNhZmVzMTguc2YgPC0gY2FmZXMxOC5zZiAlPiUKICMgICBmaWx0ZXIoIWlzLm5hKEdFT0lEKSkgJT4lCiAjICAgZ3JvdXBfYnkoR0VPSUQpICU+JQogIyAgIHN1bW1hcmlzZShjYWZlX2NvdW50ID0gbigpKSAlPiUKICMgICBtdXRhdGUoY2FmZXllYXIgPSAyMDE4KQogIyAKICMgY2FmZXMxOSA8LSBnZXRiYignUGhpbGFkZWxwaGlhLCBQQScpICU+JQogIyAgIG9wcShkYXRldGltZSA9ICcyMDE5LTAxLTAxVDAwOjAwOjAxWicpICU+JQogIyAgIGFkZF9vc21fZmVhdHVyZShrZXkgPSAiYW1lbml0eSIsIHZhbHVlID0gImNhZmUiKSAlPiUKICMgICBvc21kYXRhX3NmKCkKICMgCiAjIGNhZmVzMTkuc2YgPC0gc3RfYXNfc2YoY2FmZXMxOSRvc21fcG9pbnRzKSAlPiUKICMgICBzdF90cmFuc2Zvcm0oY3JzID0gc3RfY3JzKGNlbnN1c19wZXJtaXRzKSkgJT4lCiAjICAgICAgc3Rfam9pbihjZW5zdXNfcGVybWl0cywgam9pbiA9IHN0X3dpdGhpbikgJT4lCiAjICAgZmlsdGVyKCFpcy5uYShHRU9JRCkpICU+JQogIyAgIGdyb3VwX2J5KEdFT0lEKSAlPiUKICMgICBzdW1tYXJpc2UoY2FmZV9jb3VudCA9IG4oKSkgJT4lCiAjICAgbXV0YXRlKGNhZmV5ZWFyID0gMjAxOSkKICMgCiAjIGNhZmVzMjAgPC0gZ2V0YmIoJ1BoaWxhZGVscGhpYSwgUEEnKSAlPiUKICMgICBvcHEoZGF0ZXRpbWUgPSAnMjAyMC0wMS0wMVQwMDowMDowMVonKSAlPiUKICMgICBhZGRfb3NtX2ZlYXR1cmUoa2V5ID0gImFtZW5pdHkiLCB2YWx1ZSA9ICJjYWZlIikgJT4lCiAjICAgb3NtZGF0YV9zZigpCiAjIAogIyBjYWZlczIwLnNmIDwtIHN0X2FzX3NmKGNhZmVzMjAkb3NtX3BvaW50cykgJT4lCiAjICAgc3RfdHJhbnNmb3JtKGNycyA9IHN0X2NycyhjZW5zdXNfcGVybWl0cykpICU+JQogIyAgIHN0X2pvaW4oY2Vuc3VzX3Blcm1pdHMsIGpvaW4gPSBzdF93aXRoaW4pICU+JQogIyAgIGZpbHRlcighaXMubmEoR0VPSUQpKSAlPiUKICMgICBncm91cF9ieShHRU9JRCkgJT4lCiAjICAgc3VtbWFyaXNlKGNhZmVfY291bnQgPSBuKCkpICU+JQogIyAgIG11dGF0ZShjYWZleWVhciA9IDIwMjApCiAjIAogIyBjYWZlczIxIDwtIGdldGJiKCdQaGlsYWRlbHBoaWEsIFBBJykgJT4lCiAjICAgb3BxKGRhdGV0aW1lID0gJzIwMjEtMDEtMDFUMDA6MDA6MDFaJykgJT4lCiAjICAgYWRkX29zbV9mZWF0dXJlKGtleSA9ICJhbWVuaXR5IiwgdmFsdWUgPSAiY2FmZSIpICU+JQogIyAgIG9zbWRhdGFfc2YoKQogIyAKICMgIGNhZmVzMjEuc2YgPC0gc3RfYXNfc2YoY2FmZXMyMSRvc21fcG9pbnRzKSAlPiUKICMgICBzdF90cmFuc2Zvcm0oY3JzID0gc3RfY3JzKGNlbnN1c19wZXJtaXRzKSkgJT4lCiAjICAgc3Rfam9pbihjZW5zdXNfcGVybWl0cywgam9pbiA9IHN0X3dpdGhpbikgJT4lCiAjICAgZmlsdGVyKCFpcy5uYShHRU9JRCkpICU+JQogIyAgIGdyb3VwX2J5KEdFT0lEKSAlPiUKICMgICBzdW1tYXJpc2UoY2FmZV9jb3VudCA9IG4oKSkgJT4lCiAjICAgbXV0YXRlKGNhZmV5ZWFyID0gMjAyMSkKICMgCiAjIGNhZmVzMjIgPC0gZ2V0YmIoJ1BoaWxhZGVscGhpYSwgUEEnKSAlPiUKICMgICBvcHEoZGF0ZXRpbWUgPSAnMjAyMi0wMS0wMVQwMDowMDowMVonKSAlPiUKICMgICBhZGRfb3NtX2ZlYXR1cmUoa2V5ID0gImFtZW5pdHkiLCB2YWx1ZSA9ICJjYWZlIikgJT4lCiAjICAgb3NtZGF0YV9zZigpCiAjIAogIyBjYWZlczIyLnNmIDwtIHN0X2FzX3NmKGNhZmVzMjIkb3NtX3BvaW50cykgJT4lCiAjICAgc3RfdHJhbnNmb3JtKGNycyA9IHN0X2NycyhjZW5zdXNfcGVybWl0cykpICU+JQogIyAgIHN0X2pvaW4oY2Vuc3VzX3Blcm1pdHMsIGpvaW4gPSBzdF93aXRoaW4pICU+JQogIyAgIGZpbHRlcighaXMubmEoR0VPSUQpKSAlPiUKICMgICBncm91cF9ieShHRU9JRCkgJT4lCiAjICAgc3VtbWFyaXNlKGNhZmVfY291bnQgPSBuKCkpICU+JQogIyAgIG11dGF0ZShjYWZleWVhciA9IDIwMjIpCiAjIAogIyBjYWZlczIzIDwtIGdldGJiKCdQaGlsYWRlbHBoaWEsIFBBJykgJT4lCiAjICAgb3BxKGRhdGV0aW1lID0gJzIwMjMtMDEtMDFUMDA6MDA6MDFaJykgJT4lCiAjICAgYWRkX29zbV9mZWF0dXJlKGtleSA9ICJhbWVuaXR5IiwgdmFsdWUgPSAiY2FmZSIpICU+JQogIyAgIG9zbWRhdGFfc2YoKQogIyAKICMgY2FmZXMyMy5zZiA8LSBzdF9hc19zZihjYWZlczIzJG9zbV9wb2ludHMpICU+JQogIyAgIHN0X3RyYW5zZm9ybShjcnMgPSBzdF9jcnMoY2Vuc3VzX3Blcm1pdHMpKSAlPiUKICMgICBzdF9qb2luKGNlbnN1c19wZXJtaXRzLCBqb2luID0gc3Rfd2l0aGluKSAlPiUKICMgICBmaWx0ZXIoIWlzLm5hKEdFT0lEKSkgJT4lCiAjICAgZ3JvdXBfYnkoR0VPSUQpICU+JQogIyAgIHN1bW1hcmlzZShjYWZlX2NvdW50ID0gbigpKSAlPiUKICMgICBtdXRhdGUoY2FmZXllYXIgPSAyMDIzKQogIyAKICMgIGNhZmVzX2xpc3QgPC0gbGlzdChjYWZlczE4LnNmLCBjYWZlczE5LnNmLCBjYWZlczIwLnNmLCBjYWZlczIxLnNmLCBjYWZlczIyLnNmLAogIyAgICAgICAgICAgICAgICAgICAgIGNhZmVzMjMuc2YpCiAjIAogIyBhbGxfY2FmZXMgPC0gcmVkdWNlKGNhZmVzX2xpc3QsIGJpbmRfcm93cykgJT4lCiAjICAgcmVuYW1lKCJ5ZWFyIiA9ICJjYWZleWVhciIpICU+JQogIyAgc3RfZHJvcF9nZW9tZXRyeSgpCiAjIAogIyAKICMgd3JpdGUuY3N2KGFsbF9jYWZlcywgIkRhdGEvYWxsX2NhZmVzLmNzdiIpCiAjIAoKIyBybShjYWZlMTgsIGNhZmVzMTguc2YsIGNhZmVzMTksIGNhZmVzMTkuc2YsIGNhZmVzMjAsIGNhZmVzMjAuc2YsIGNhZmVzMjEsIGNhZmVzMjEuc2YsIGNhZmVzMjIsIGNhZmVzMjIuc2YsIGNhZmVzMjMsIGNhZmVzMjMuc2YpCgogYWxsX2NhZmVzIDwtIHJlYWQuY3N2KCJEYXRhL2FsbF9jYWZlcy5jc3YiKSAlPiUKICAgc2VsZWN0KC1YKQoKYWxsX2NhZmVzJEdFT0lEIDwtICBhcy5jaGFyYWN0ZXIoYWxsX2NhZmVzJEdFT0lEKQoKCiAgY2Vuc3VzX3Blcm1pdHMgPC0gY2Vuc3VzX3Blcm1pdHMgJT4lCiAgICBsZWZ0X2pvaW4oYWxsX2NhZmVzLCBieSA9IGMoIkdFT0lEIiwgInllYXIiKSkgJT4lCiAgICAgbXV0YXRlKGNhZmVfY291bnQgPSBpZmVsc2UoaXMubmEoY2FmZV9jb3VudCksIDAsIGNhZmVfY291bnQpKQoKYGBgCgojIyMjIERpc3RhbmNlIHRvIHRyYW5zaXQgc3RhdGlvbnMKCldlIGFsc28gbG9va2VkIGF0IHByb3hpbWl0eSB0byBmaXhlZCBwdWJsaWMgdHJhbnNwb3J0YXRpb24gd2l0aGluIHRoZSBjaXR5IG9mIFBoaWxhZGVscGhpYSwgaW5jbHVkaW5nIHJlZ2lvbmFsIHJhaWwsIHRoZSBCcm9hZCBTdHJlZXQgTGluZSwgTWFya2V0IEZyYW5rZm9yZCBMaW5lLCBhbmQgdHJvbGxleSBsaW5lcy4gCgpgYGB7ciByZXN1bHRzID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KcmVnaW9uYWxyYWlsIDwtIHN0X3JlYWQoIkRhdGEvUmVnaW9uYWxfUmFpbF9TdGF0aW9ucy5nZW9qc29uIikgCgp0cm9sbGV5cyA8LSBzdF9yZWFkKCJEYXRhL1Ryb2xsZXlfU3RhdGlvbnMuZ2VvanNvbiIpICU+JQogIHJlbmFtZShMaW5lX05hbWUgPSBMaW5lQWJiciwKICAgICAgICAgTGF0aXR1ZGUgPSBMYXQsCiAgICAgICAgIExvbmdpdHVkZSA9IExvbiwKICAgICAgICAgU3RhdGlvbl9OYSA9IFN0b3BOYW1lKSAlPiUKICBzZWxlY3QoRklELCBMaW5lX05hbWUsIFN0YXRpb25fTmEsIExhdGl0dWRlLCBMb25naXR1ZGUsIGdlb21ldHJ5KQogIAptZXRybyA8LSBzdF9yZWFkKCJEYXRhL0hpZ2hzcGVlZF9TdGF0aW9ucy5nZW9qc29uIikgJT4lCiAgcmVuYW1lKExpbmVfTmFtZSA9IFJvdXRlLCAKICAgICAgICAgU3RhdGlvbl9OYSA9IFN0YXRpb24pCgp0cmFuc2l0IDwtIHJiaW5kKHJlZ2lvbmFscmFpbCwgdHJvbGxleXMsIG1ldHJvKSAlPiUKICAgIHN0X3RyYW5zZm9ybShjcnMgPSBzdF9jcnMocGhsX3RyYWN0czIyKSkKCgpwaGxfdHJhbnNpdCA8LSBzdF9qb2luKHRyYW5zaXQsIHBobF90cmFjdHMyMiwgam9pbiA9IHN0X3dpdGhpbikgJT4lCiAgZmlsdGVyKCFpcy5uYShHRU9JRCkpICU+JQogIHNlbGVjdChMaW5lX05hbWUsIFN0YXRpb25fTmEsIExhdGl0dWRlLCBMb25naXR1ZGUsIEdFT0lELCBnZW9tZXRyeSkgJT4lCiAgc3RfdHJhbnNmb3JtKGNycyA9IHN0X2NycyhjZW5zdXNfcGVybWl0cykpCgpjZW5zdXNfcGVybWl0cyA8LSBjZW5zdXNfcGVybWl0cyAlPiUKICBtdXRhdGUobmVhcmVzdF90cmFuc2l0ID0gbm5fZnVuY3Rpb24oc3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoY2Vuc3VzX3Blcm1pdHMpKSwgc3RfY29vcmRpbmF0ZXMocGhsX3RyYW5zaXQpLCBrPTEpKjY5LjIpCmBgYAoKIyMjIyBOdW1iZXIgb2Ygam9icyB3aXRoaW4gdHJhY3QKCkZpbmFsbHksIHdlIGxvb2tlZCBhdCBqb2IgY291bnRzIHBlciB0cmFjdC4gQWx0aG91Z2ggd2UgaW5pdGlhbGx5IHdlcmUgZ29pbmcgdG8gbG9vayBhdCBwbGFjZXMgc3VjaCBhcyBtYWpvciB1bml2ZXJzaXRpZXMgb3IgZGlzdGFuY2UgdG8gY2VudGVyIGNpdHksIHVsdGltYXRlbHksIHdlIHJlY29nbml6ZWQgdGhhdCB0aGVzZSBsb2NhdGlvbnMgYXJlIHN1YnN0aXR1dGVzIGZvciBqb2IgZGVuc2l0eS4gV2UgbG9va2VkIGF0IGpvYnMgb3ZlciB0aW1lLCBidXQgb3ZlcmFsbCB0aGUgcHJvcG9ydGlvbiByZW1haW5zIHByZXR0eSBzaW1pbGFyIGFjcm9zcyB0aGUgeWVhcnMgZm9yIHdoaWNoIHdlIGhhdmUgZGF0YS4gVGhlcmVmb3JlLCB3ZSBbaW50ZXJwb2xhdGVdKGh0dHBzOi8vd2Fsa2VyLWRhdGEuY29tL3RpZHljZW5zdXMvcmVmZXJlbmNlL2ludGVycG9sYXRlX3B3Lmh0bWwKaHR0cHM6Ly93YWxrZXItZGF0YS5jb20vdGlkeWNlbnN1cy9yZWZlcmVuY2UvaW50ZXJwb2xhdGVfcHcuaHRtbCkgdGhlIGdlb2dyYXBoaWVzIHRvIDIwMjAgdHJhY3RzIGFuZCB0aGVuIGxvb2sgYXQgc3BhdGlhbCBsYWcgLS0gam9icyBpbiBzdXJyb3VuZGluZyBjZW5zdXMgdHJhY3RzLiBUaGlzIGZlYXR1cmUgY291bGQgdXNlIHNvbWUgZnVydGhlciBkZXZlbG9wbWVudCwgYmVjYXVzZSBvdXIgdmlzdWFsIGFzc2Vzc21lbnQgaXMgdGhhdCBwZW9wbGUgZG8gbm90IHdhbnQgdG8gbGl2ZSBpbiB0aGUgYXJlYXMgd2l0aCB0aGUgaGlnaGVzdCBqb2IgZGVuc2l0eSwgYnV0IGEgd2l0aGluIGEgY2VydGFpbiBkaXN0YW5jZSBvZiB0aG9zZSBqb2IgY2VudGVycy4gCgpgYGB7cn0KI3VzZSBMZWhkciBwYWNrYWdlIHRvIGJyaW5nIGluIGpvYnMgYW5kIHRvIGFuYWx5emUgd2hlcmUgcGVvcGxlIGFyZSBnb2luZyBieSB3b3JrIGFuZCBob21lIGNlbnN1cyB0cmFjdC4gCiNkZXJpdmVkIGZyb206IGh0dHBzOi8vZ2l0aHViLmNvbS9qYW1ncmVlbi9sZWhkciAKCiNwYSBqb2JzIGZyb20gTEVIRFIKI21hdGNoIHdpdGggbG9kZXMgZGF0YSBmcm9tIHByZXZpb3VzIHllYXIsIGdyYWIgZGF0YSBmb3IgMjAxOSAtIDIwMjIKI21hdGNoIHRoZSBpbmRpdmlkdWFsIHBlcm1pdCBzaGVldCB3aXRoIHRoZSBwcmV2aW91cyB5ZWFycyBqb2IgZGF0YQojIAojIHBhX2pvYnMxOSA8LSBncmFiX2xvZGVzKHN0YXRlID0gInBhIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSAyMDE5LAojICAgICAgICAgICAgICAgICAgICAgICAgdmVyc2lvbiA9ICJMT0RFUzgiLAojICAgICAgICAgICAgICAgICAgICAgICAgbG9kZXNfdHlwZSA9ICJvZCIsCiMgICAgICAgICAgICAgICAgICAgICAgICBqb2JfdHlwZSA9ICJKVDAxIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIHNlZ21lbnQgPSAiUzAwMCIsCiMgICAgICAgICAgICAgICAgICAgICAgICBzdGF0ZV9wYXJ0ID0gIm1haW4iLAojICAgICAgICAgICAgICAgICAgICAgICAgYWdnX2dlbyA9ICJ0cmFjdCIpICU+JQojICAgc2VsZWN0KC1oX3RyYWN0KQojIAojIHBhX2pvYnMyMCA8LSBncmFiX2xvZGVzKHN0YXRlID0gInBhIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSAyMDIwLAojICAgICAgICAgICAgICAgICAgICAgICAgdmVyc2lvbiA9ICJMT0RFUzgiLAojICAgICAgICAgICAgICAgICAgICAgICAgbG9kZXNfdHlwZSA9ICJvZCIsCiMgICAgICAgICAgICAgICAgICAgICAgICBqb2JfdHlwZSA9ICJKVDAxIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIHNlZ21lbnQgPSAiUzAwMCIsCiMgICAgICAgICAgICAgICAgICAgICAgICBzdGF0ZV9wYXJ0ID0gIm1haW4iLAojICAgICAgICAgICAgICAgICAgICAgICAgYWdnX2dlbyA9ICJ0cmFjdCIpJT4lCiMgICBzZWxlY3QoLWhfdHJhY3QpCiMgCiMgcGFfam9iczIxIDwtIGdyYWJfbG9kZXMoc3RhdGUgPSAicGEiLAojICAgICAgICAgICAgICAgICAgICAgICAgeWVhciA9IDIwMjEsCiMgICAgICAgICAgICAgICAgICAgICAgICB2ZXJzaW9uID0gIkxPREVTOCIsCiMgICAgICAgICAgICAgICAgICAgICAgICBsb2Rlc190eXBlID0gIm9kIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIGpvYl90eXBlID0gIkpUMDEiLAojICAgICAgICAgICAgICAgICAgICAgICAgc2VnbWVudCA9ICJTMDAwIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRlX3BhcnQgPSAibWFpbiIsCiMgICAgICAgICAgICAgICAgICAgICAgICBhZ2dfZ2VvID0gInRyYWN0IiklPiUKIyAgIHNlbGVjdCgtaF90cmFjdCkKIyAKIyAjZmlsdGVyIGZvciBvbmx5IGpvYnMgdGhhdCBzdGFydCBvciBlbmQgaW4gUGhpbGFkZWxwaGlhIHRyYWN0cwojIAojIHBobF9qb2JzMTkgPC0gcGFfam9iczE5ICU+JQojICAgbGVmdF9qb2luKHBobF90cmFjdHMyMiwgYnkgPSBjKCJ3X3RyYWN0IiA9ICJHRU9JRCIpKSAlPiUKIyAgICAgZmlsdGVyKCFzdF9pc19lbXB0eShnZW9tZXRyeSkpICU+JQojICAgc3RfYXNfc2YoKSAlPiUKIyAgIHJlbmFtZShHRU9JRCA9IHdfdHJhY3QpICU+JQojICAgZ3JvdXBfYnkoR0VPSUQpICU+JQojICAgc3VtbWFyaXNlKGpvYl9jb3VudCA9IG4oKSkKIyAKIyBwaGxfam9iczE5IDwtIGludGVycG9sYXRlX3B3KAojICAgcGhsX2pvYnMxOSwKIyAgIHBobF90cmFjdHMyMiwKIyAgIHRvX2lkID0gIkdFT0lEIiwKIyAgIGV4dGVuc2l2ZSA9IFRSVUUsIAojICAgd2VpZ2h0cyA9IHBobF90cmFjdHMyMiwKIyAgIHdlaWdodF9jb2x1bW4gPSAiVG90YWxfUG9wIiwKIyAgIGNycyA9IDQyNjkpCiMgCiMgcGhsX2pvYnMyMCA8LSBwYV9qb2JzMjAgJT4lCiMgICBsZWZ0X2pvaW4ocGhsX3RyYWN0czIyLCBieSA9IGMoIndfdHJhY3QiID0gIkdFT0lEIikpICU+JQojICAgICBmaWx0ZXIoIXN0X2lzX2VtcHR5KGdlb21ldHJ5KSkgJT4lCiMgICBzdF9hc19zZigpICU+JQojICAgcmVuYW1lKEdFT0lEID0gd190cmFjdCkgJT4lCiMgICBncm91cF9ieShHRU9JRCkgJT4lCiMgICBzdW1tYXJpc2Uoam9iX2NvdW50ID0gbigpKQojIAojIHBobF9qb2JzMjEgPC0gcGFfam9iczIxICU+JQojICAgbGVmdF9qb2luKHBobF90cmFjdHMyMiwgYnkgPSBjKCJ3X3RyYWN0IiA9ICJHRU9JRCIpKSAlPiUKIyAgICAgZmlsdGVyKCFzdF9pc19lbXB0eShnZW9tZXRyeSkpICU+JQojICAgc3RfYXNfc2YoKSAlPiUKIyAgIHJlbmFtZShHRU9JRCA9IHdfdHJhY3QpICU+JQojICAgZ3JvdXBfYnkoR0VPSUQpICU+JQojICAgc3VtbWFyaXNlKGpvYl9jb3VudCA9IG4oKSkKIyAKIyBwaGxfam9ic19jb21iaW5lZCA8LSByYmluZChjYmluZChwaGxfam9iczE5LCB5ZWFyID0gMjAxOSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2JpbmQocGhsX2pvYnMyMCwgeWVhciA9IDIwMjApLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNiaW5kKHBobF9qb2JzMjEsIHllYXIgPSAyMDIxKSkKIyAKIyBnZ3Bsb3QocGhsX2pvYnNfY29tYmluZWQpICsKIyAgIGdlb21fc2YoYWVzKGZpbGwgPSBqb2JfY291bnQpKSArCiMgICBsYWJzKHRpdGxlID0gIkpvYiBEaXN0cmlidXRpb24gaW4gUGhpbGFkZWxwaGlhIiwgY29sb3IgPSAiSm9iIENvdW50IikgKwojICAgbWFwVGhlbWUoKSArCiMgICBmYWNldF93cmFwKH55ZWFyLCBuY29sID0gMykKIyAKIyBwaGxfam9ic19jb21iaW5lZCR5ZWFyIDwtIGFzLk51bWVyaWMocGhsX2pvYnNfY29tYmluZWQkeWVhcikKCnBobF9qb2JzX2NvbWJpbmVkIDwtIHJlYWQuY3N2KCJEYXRhL3BobF9qb2JzLmNzdiIpICU+JQogIHNlbGVjdCgtWCkgJT4lCiAgbXV0YXRlKEdFT0lEID0gYXMuY2hhcmFjdGVyKEdFT0lEKSkgJT4lCiAgcmVuYW1lKCJ5ZWFyX2pvYnMiID0gInllYXIiKQoKY2Vuc3VzX3Blcm1pdHMgPC0gY2Vuc3VzX3Blcm1pdHMgJT4lCiAgbXV0YXRlKHllYXJfam9icyA9IGNhc2Vfd2hlbih5ZWFyID09IDIwMTggfiAyMDE5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciA9PSAyMDIyIH4gMjAyMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyID09IDIwMjMgfiAyMDIxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDIwMTksMjAyMCwyMDIxKSB+IHllYXIpKSAlPiUKICBsZWZ0X2pvaW4ocGhsX2pvYnNfY29tYmluZWQsIAogICAgICAgICAgICBieSA9IGMoIkdFT0lEIiwieWVhcl9qb2JzIikpICU+JQogIHNlbGVjdCgteWVhcl9qb2JzKQoKcGhsX2pvYnNfY29tYmluZWQgPC0gcGhsX2pvYnNfY29tYmluZWQgJT4lCiAgbGVmdF9qb2luKHBobF90cmFjdHMyMiwgYnkgPSBjKCJHRU9JRCIgPSAiR0VPSUQiKSkgJT4lCiAgc2VsZWN0KGpvYl9jb3VudCwgR0VPSUQsIGdlb21ldHJ5KSAlPiUKICBzdF9hc19zZigpCgpuYiA8LSBzdF9jb250aWd1aXR5KGNlbnN1c19wZXJtaXRzJGdlb21ldHJ5LCBxdWVlbiA9IFRSVUUpCnd0IDwtIHN0X3dlaWdodHMobmIpCgpjZW5zdXNfcGVybWl0cyA8LSBjZW5zdXNfcGVybWl0cyAlPiUKICAgbXV0YXRlKGpvYnMubm49IHN0X2xhZyhqb2JfY291bnQsbmIsd3QsIG5hX29rID0gVFJVRSkpCiAgCmBgYAoKIyMjIyBOZWlnaGJvcmhvb2RzCgpgYGB7ciByZXN1bHRzID0gJ2hpZGUnfQpjZW5zdXNfcGVybWl0cyA8LSBzdF9qb2luKHN0X2NlbnRyb2lkKGNlbnN1c19wZXJtaXRzKSwgcGhsLm5oLCBqb2luID0gc3Rfd2l0aGluKSAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgbGVmdF9qb2luKHBobF90cmFjdHM0MzI2KSAlPiUKICBzdF9hc19zZigpCmBgYAoKIyMjICoqRVhQTE9SQVRPUlkgQU5BTFlTSVMqKgoKSGVyZSwgd2UgbG9vayBhdCBvdXIgZGVwZW5kZW50IHZhcmlhYmxlLCB0b3RhbCBwZXJtaXRzLCBhcyBhIGZ1bmN0aW9uIG9mIG91ciBudW1lcmljIHZhcmlhYmxlcy4gTGFzdCB5ZWFyJ3MgcGVybWl0cyBhbmQgdGhlIExvY2FsIE1vcmFuJ3MgSSBhcmUgbW9zdCBzdHJvbmdseSBjb3JyZWxhdGVkLiAKClNvbWUgb2YgdGhlc2UgZ3JhcGhzIHNob3cgdGhhdCBwZXJtaXRzIHNwaWtlIGZvciB2YWx1ZXMgaW4gdGhlIG1pZGRsZSBvZiB0aGUgZGlzdHJpYnV0aW9uLiBBcyBhbiBleHBlcmltZW50LCB3ZSBjb3VsZCBtYWtlIGEgYmluYXJ5IHZhcmlhYmxlIGZvciByZWxhdGlvbnNoaXBzIHdoZXJlIHRoZXJlJ3MgYSBzcGlrZSBpbiB0aGUgbWlkZGxlIC0gYmV0d2VlbiB0aGUgNDV0aCBhbmQgNTV0aCBwZXJjZW50aWxlIG9yIG5vdC4gCgpgYGB7ciwgZmlnLmhlaWdodD0zMCwgZmlnLndpZHRoID0gMTB9CgojZmlsdGVyIHRoZSBvdXRsaWVycyAKY2Vuc3VzX3Blcm1pdHNfdGVzdCA8LSBjZW5zdXNfcGVybWl0cyAlPiUKICBmaWx0ZXIoVE9UQUwgPCA2MDApICU+JQogIGRwbHlyOjpzZWxlY3QoLWluZGljYXRvciwgLW1hcG5hbWUsIC1uYW1lLCAtU2lnbmlmaWNhbnRfSG90c3BvdHMpICU+JQogIGdhdGhlcih2YXJpYWJsZSwgdmFsdWUsIC1nZW9tZXRyeSwgLXllYXIsIC1HRU9JRCwgLVRPVEFMKQoKZ2dwbG90KGNlbnN1c19wZXJtaXRzX3Rlc3QsIGFlcyh2YWx1ZSwgVE9UQUwpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzdGF0X3Ntb290aChhZXModmFsdWUsIFRPVEFMKSwgCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgc2l6ZSA9IDEsIGNvbG91cj0iI2ZmYjYwMCIpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzPSAiZnJlZSIsIG5jb2w9NCkgKwogIGxhYnModGl0bGUgPSAiRXhwbG9yYXRvcnkgQW5hbHlzaXMiLCBjYXB0aW9uID0gIkZpZ3VyZSA4IikKCmBgYAoKV2UgY2FuIHRlc3QgdGhpcyB0aGVvcnkgb3V0IG9uIHRoZSB2YXJpYWJsZXMgdGhhdCBhcHBlYXIgdG8gaGF2ZSB0aGUgbGFyZ2VzdCBzcGlrZXMgaW4gdGhlIG1pZGRsZS4gCkl0IHR1cm5zIG91dCB0aGlzIGlzIG9ubHkgdHJ1ZSBmb3IgdGhyZWUgdmFyaWFibGVzIC0tIHRoZSBtaWRkbGUgcGVyY2VudGlsZSAoYmV0d2VlbiA0NXRoIGFuZCA1NXRoIHBlcmNlbnRpbGUpIGlzIHJlbGF0ZWQgdG8gdGhlIG51bWJlciBvZiBwZXJtaXRzIGZvciB0aGUgY2hhbmdlIGluIHBlcmNlbnRhZ2Ugb2YgaG9tZXMgd2l0aCBhIHNlY29uZCBtb3J0Z2FnZSBiZXR3ZWVuIDIwMTcgYW5kIDIwMjIgNS1ZZWFyIEFDUyBzdXJ2ZXlzLCBmb3IgMjAxNyBhZGp1c3RlZCBtZWFuIHJlbnQsIGFuZCBmb3IgcGVyY2VudCBvd25lciBpbiAyMDIyLgoKYGBge3J9CmNlbnN1c19wZXJtaXRzX3Rlc3QgPC0gY2Vuc3VzX3Blcm1pdHMgJT4lCiAgbXV0YXRlKAogICAgbWlkZGxlX2JhY2hfMTcgPSBpZmVsc2UoQmFjaF8yMDE3ID4gcXVhbnRpbGUoQmFjaF8yMDE3LCAuNDUsIG5hLnJtID0gVFJVRSkgJiBCYWNoXzIwMTcgPD0gcXVhbnRpbGUoQmFjaF8yMDE3LCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgI21heWJlIHRoaXMgb25lCiAgICAgICAgIG1pZGRsZV9yZW50XzE3ID0gaWZlbHNlKE1lZF9SZW50XzIwMTcgPiBxdWFudGlsZShNZWRfUmVudF8yMDE3LCAuNDUsIG5hLnJtID0gVFJVRSkgJiBNZWRfUmVudF8yMDE3IDw9IHF1YW50aWxlKE1lZF9SZW50XzIwMTcsIC41NSwgbmEucm0gPSBUUlVFKSwgVFJVRSwgRkFMU0UpLAogICAgCiAgICAgICAgIG1pZGRsZV9wcmljZV9ORVQgPSBpZmVsc2UoTWVkX1ZhbHVlX05FVCA+IHF1YW50aWxlKE1lZF9WYWx1ZV9ORVQsIC40NSwgbmEucm0gPSBUUlVFKSAmIE1lZF9WYWx1ZV9ORVQgPD0gcXVhbnRpbGUoTWVkX1ZhbHVlX05FVCwgLjU1LCBuYS5ybSA9IFRSVUUpLCBUUlVFLCBGQUxTRSksCiAgICAKICAgICAgICAgbWlkZGxlX25uX2JhY2hfMjIgPSBpZmVsc2Uobm5fYmFjaF8yMiA+IHF1YW50aWxlKG5uX2JhY2hfMjIsIC40NSwgbmEucm0gPSBUUlVFKSAmIG5uX2JhY2hfMjIgPD0gcXVhbnRpbGUobm5fYmFjaF8yMiwgLjU1LCBuYS5ybSA9IFRSVUUpLCBUUlVFLCBGQUxTRSksCiAgICAKICAgICAgICAgbWlkZGxlX25uX2luY29tZV8yMiA9IGlmZWxzZShubl9pbmNvbWVfMjIgPiBxdWFudGlsZShubl9pbmNvbWVfMjIsIC40NSwgbmEucm0gPSBUUlVFKSAmIG5uX2luY29tZV8yMiA8PSBxdWFudGlsZShubl9pbmNvbWVfMjIsIC41NSwgbmEucm0gPSBUUlVFKSwgVFJVRSwgRkFMU0UpLAogICAgCiAgICAgICAgIG1pZGRsZV9ubl9tZWFuX3ByaWNlXzIyID0gaWZlbHNlKG5uX21lYW5fcHJpY2VfMjIgPiBxdWFudGlsZShubl9tZWFuX3ByaWNlXzIyLCAuNDUsIG5hLnJtID0gVFJVRSkgJiBubl9tZWFuX3ByaWNlXzIyIDw9IHF1YW50aWxlKG5uX21lYW5fcHJpY2VfMjIsIC41NSwgbmEucm0gPSBUUlVFKSwgVFJVRSwgRkFMU0UpLAogICAgCiAgICAgICAgIG1pZGRsZV9ubl9tZWFuX3JlbnRfMjIgPSBpZmVsc2Uobm5fbWVhbl9yZW50XzIyID4gcXVhbnRpbGUobm5fbWVhbl9yZW50XzIyLCAuNDUsIG5hLnJtID0gVFJVRSkgJiBubl9tZWFuX3JlbnRfMjIgPD0gcXVhbnRpbGUobm5fbWVhbl9yZW50XzIyLCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgICAgICBtaWRkbGVfT3duZXJzX1BDVCA9IGlmZWxzZShPd25lcnNfUENUID4gcXVhbnRpbGUoT3duZXJzX1BDVCwgLjQ1LCBuYS5ybSA9IFRSVUUpICYgT3duZXJzX1BDVCA8PSBxdWFudGlsZShPd25lcnNfUENULCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgI3RoaXMgb25lCiAgICAgICAgIG1pZGRsZV9QZXJjZW50XzJtb3J0X05FVCA9IGlmZWxzZShQZXJjZW50XzJtb3J0X05FVCA+IHF1YW50aWxlKFBlcmNlbnRfMm1vcnRfTkVULCAuNDUsIG5hLnJtID0gVFJVRSkgJiBQZXJjZW50XzJtb3J0X05FVCA8PSBxdWFudGlsZShQZXJjZW50XzJtb3J0X05FVCwgLjU1LCBuYS5ybSA9IFRSVUUpLCBUUlVFLCBGQUxTRSksCiAgICAKICAgICAgICAgbWlkZGxlX1BlcmNlbnRfYmFjaF8yMDIyID0gaWZlbHNlKFBlcmNlbnRfYmFjaF8yMDIyID4gcXVhbnRpbGUoUGVyY2VudF9iYWNoXzIwMjIsIC40NSwgbmEucm0gPSBUUlVFKSAmIFBlcmNlbnRfYmFjaF8yMDIyIDw9IHF1YW50aWxlKFBlcmNlbnRfYmFjaF8yMDIyLCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgICAgICBtaWRkbGVfUGVyY2VudF9iYWNoX1BDVCA9IGlmZWxzZShQZXJjZW50X2JhY2hfUENUID4gcXVhbnRpbGUoUGVyY2VudF9iYWNoX1BDVCwgLjQ1LCBuYS5ybSA9IFRSVUUpICYgUGVyY2VudF9iYWNoX1BDVCA8PSBxdWFudGlsZShQZXJjZW50X2JhY2hfUENULCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgI3RoaXMgb25lCiAgICAgICAgIG1pZGRsZV9QZXJjZW50X093bmVyXzIwMjIgPSBpZmVsc2UoUGVyY2VudF9Pd25lcl8yMDIyID4gcXVhbnRpbGUoUGVyY2VudF9Pd25lcl8yMDIyLCAuNDUsIG5hLnJtID0gVFJVRSkgJiBQZXJjZW50X093bmVyXzIwMjIgPD0gcXVhbnRpbGUoUGVyY2VudF9Pd25lcl8yMDIyLCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgICAgICBtaWRkbGVfUmVudF9JbmNvbWVfUmF0aW9fMjAxNyA9IGlmZWxzZShSZW50X0luY29tZV9SYXRpb18yMDE3ID4gcXVhbnRpbGUoUmVudF9JbmNvbWVfUmF0aW9fMjAxNywgLjQ1LCBuYS5ybSA9IFRSVUUpICYgUmVudF9JbmNvbWVfUmF0aW9fMjAxNyA8PSBxdWFudGlsZShSZW50X0luY29tZV9SYXRpb18yMDE3LCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgI0Fsc28gdGVzdGluZyB0aGlzIG5ldyBiaW5hcnkgdmFyaWFibGUhCiAgICB0cmFuc2l0X3VuZGVyXzEuNSA9IGlmZWxzZShuZWFyZXN0X3RyYW5zaXQgPD0gMS41LCBUUlVFLCBGQUxTRSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUKICAKICBkcGx5cjo6c2VsZWN0KFRPVEFMLCBzdGFydHNfd2l0aCgnbWlkZGxlJyksIHRyYW5zaXRfdW5kZXJfMS41KSAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgZ2F0aGVyKHZhcmlhYmxlLCB2YWx1ZSwgLVRPVEFMKQoKY2Vuc3VzX3Blcm1pdHNfdGVzdCAlPiUKICBmaWx0ZXIoVE9UQUwgPCA2MDApICU+JQogIGdyb3VwX2J5KHZhcmlhYmxlLCB2YWx1ZSkgJT4lCiAgc3VtbWFyaXNlKG1lYW5fcGVybWl0cyA9IG1lYW4oVE9UQUwpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgICBnZ3Bsb3QoYWVzKHZhbHVlLG1lYW5fcGVybWl0cykpICsgCiAgICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdD0gImlkZW50aXR5IiwgZmlsbCA9ICIjZmZiNjAwIikgKyAKICAgICAgZmFjZXRfd3JhcCh+dmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIikgKwogICAgICBsYWJzKHg9IlRvdGFsIFBlcm1pdHMiLCB5PSJWYWx1ZSIsIAogICAgICAgICAgIHRpdGxlID0gIk1pZGRsZSBwZXJjZW50aWxlICg0NS01NXRoKSBhc3NvY2lhdGlvbnMgd2l0aCBudW1iZXIgb2YgcGVybWl0cyIsIGNhcHRpb24gPSAiRmlndXJlIDkiKSArCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgCgojIE11dGF0aW5nIG9ubHkgZm9yIHRoZSB2YXJpYWJsZXMgdmFyaWFibGVzIHRoYXQgbWF0dGVyZWQKCmNlbnN1c19wZXJtaXRzIDwtIGNlbnN1c19wZXJtaXRzICU+JQogIG11dGF0ZShtaWRkbGVfUGVyY2VudF9Pd25lcl8yMDIyID0gaWZlbHNlKFBlcmNlbnRfT3duZXJfMjAyMiA+IHF1YW50aWxlKFBlcmNlbnRfT3duZXJfMjAyMiwgLjQ1LCBuYS5ybSA9IFRSVUUpICYgUGVyY2VudF9Pd25lcl8yMDIyIDw9IHF1YW50aWxlKFBlcmNlbnRfT3duZXJfMjAyMiwgLjU1LCBuYS5ybSA9IFRSVUUpLCBUUlVFLCBGQUxTRSksCiAgICAgICAgIAogICAgICAgICBtaWRkbGVfUGVyY2VudF8ybW9ydF9ORVQgPSBpZmVsc2UoUGVyY2VudF8ybW9ydF9ORVQgPiBxdWFudGlsZShQZXJjZW50XzJtb3J0X05FVCwgLjQ1LCBuYS5ybSA9IFRSVUUpICYgUGVyY2VudF8ybW9ydF9ORVQgPD0gcXVhbnRpbGUoUGVyY2VudF8ybW9ydF9ORVQsIC41NSwgbmEucm0gPSBUUlVFKSwgVFJVRSwgRkFMU0UpLAogICAgICAgICAKICAgICAgICAgIG1pZGRsZV9yZW50XzE3ID0gaWZlbHNlKE1lZF9SZW50XzIwMTcgPiBxdWFudGlsZShNZWRfUmVudF8yMDE3LCAuNDUsIG5hLnJtID0gVFJVRSkgJiBNZWRfUmVudF8yMDE3IDw9IHF1YW50aWxlKE1lZF9SZW50XzIwMTcsIC41NSwgbmEucm0gPSBUUlVFKSwgVFJVRSwgRkFMU0UpLAogICAgICAgICAKICAgICAgICAgdHJhbnNpdF91bmRlcl8xLjUgPSBpZmVsc2UobmVhcmVzdF90cmFuc2l0IDw9IDEuNSwgVFJVRSwgRkFMU0UpKQpgYGAKCgpgYGB7cn0KI01hcHBpbmcgY2FmZXMgYW5kIHZhY2FuY3k6CgojIGdncGxvdChjZW5zdXNfcGVybWl0cykgKwojICAgZ2VvbV9zZihhZXMoZmlsbCA9IGNhZmVfY291bnQpKSArCiMgICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBDYWZlcyBwZXIgVHJhY3QiKSArCiMgICBmYWNldF93cmFwKH55ZWFyKSArCiMgICBtYXBUaGVtZSgpCiMgCiMgZ2dwbG90KGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIzKSkgKwojICAgZ2VvbV9zZihhZXMoZmlsbCA9IFBlcmNlbnRfVmFjYW50XzIwMTYpKSArCiMgICBsYWJzKHRpdGxlID0gIlZhY2FuY3kgUmF0ZSAyMDE2IikgKwojICAgbWFwVGhlbWUoKQoKYGBgCgojIyMjIENvcnJlbGF0aW9uIE1hdHJpeAoKV2UgY29tcGFyZWQgdmFyaWFibGVzIGFnYWluc3QgZWFjaCBvdGhlciBpbiBhIGNvcnJlbGF0aW9uIHRlc3QuIE1hbnkgb2YgdGhlIHZhcmlhYmxlcyBjb3JyZWxhdGVkIHdpdGggZWFjaCBvdGhlciBtb3JlIHRoYW4gd2l0aCB0aGUgY291bnQgb2YgdG90YWwgcGVybWl0cy4gVGhlIG1vc3QgY29ycmVsYXRlZCB2YXJpYWJsZXMgd2l0aCBwZXJtaXQgY291bnQgYXJlIHZhY2FuY3kgd2l0aCBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uLCB3aGVyZWluIG1vcmUgdmFjYW5jeSBtZWFucyBoaWdoZXIgcGVybWl0IGlzc3VhbmNlLiBBZnRlciBzb21lIGFkZGl0aW9uYWwgYW5hbHlzaXMgYW5kIGZlYXR1cmUgZW5naW5lZXJpbmcgaG93ZXZlciwgd2UgcmVhbGl6ZSB0aGF0IGNsb3NlIHByb3hpbWl0eSB0byB0cmFuc2l0ICh3aXRoaW4gMS41IG1pbGUpIGlzIG1vcmUgc2lnbmlmaWNhbnQgdGhhbiB0aGUgbmVhcmVzdCBuZWlnaGJvciB0cmFuc2l0LiBWYXJpYWJsZXMgYXJlIG9mIGNvdXJzZSBjb3JyZWxhdGVkIHdpdGggZWFjaCBvdGhlciwgYnV0IG92ZXJhbGwgY2Vuc3VzIHZhcmlhYmxlcyBhcmUgbm90IGFzIGNvcnJlbGF0ZWQgd2l0aCBwZXJtaXQgY291bnQgYXMgZXhwZWN0ZWQuIAoKYGBge3J9CgpudW1lcmljVmFycyA8LSBjZW5zdXNfcGVybWl0cyAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgc2VsZWN0KFRPVEFMLCBjYWZlX2NvdW50LCBuZWFyZXN0X3RyYW5zaXQsIGpvYl9jb3VudCwgTWVkX0luY18yMDIyLCBSZW50X0luY29tZV9SYXRpb19ORVQsCiAgICAgICAgIFdoaXRlX1BvcF8yMDIyLCBNZWRfVmFsdWVfTkVULCBWYWNhbnRzXzIwMjIsIFZhY2FudHNfMjAxNywKICAgICAgICAgVmFjYW50c19QQ1QsIFZhY2FudHNfTkVUKSAKCnByaWNlLmNvcnIgPC0gY29yci50ZXN0KG51bWVyaWNWYXJzKQoKaWYgKHN1bShpcy5uYShudW1lcmljVmFycykpID4gMCkgewogICMgSGFuZGxlIG1pc3NpbmcgdmFsdWVzIChlLmcuLCBpbXB1dGUgb3IgcmVtb3ZlKQogIG51bWVyaWNWYXJzIDwtIG5hLm9taXQobnVtZXJpY1ZhcnMpICAjIFJlbW92ZSByb3dzIHdpdGggbWlzc2luZyB2YWx1ZXMKfQoKY29ycmVsYXRpb25fbWF0cml4IDwtIHJvdW5kKGNvcihudW1lcmljVmFycyksIDEpCnBfdmFsdWVzIDwtIGNvcl9wbWF0KG51bWVyaWNWYXJzKQoKZ2djb3JycGxvdCgKICBjb3JyZWxhdGlvbl9tYXRyaXgsCiAgcC5tYXQgPSBwX3ZhbHVlcywKICBjb2xvcnMgPSBjKCAiI2YyMDA4OSIsICJ3aGl0ZSIsICIjMmQwMGY3IiksCiAgdHlwZSA9ICJsb3dlciIsCiAgaW5zaWcgPSAiYmxhbmsiCiAgCikgKwpsYWJzKHRpdGxlID0gIkRlZ3JlZXMgb2YgQ29ycmVsYXRpb24gdG8gUGVybWl0cyBJc3N1ZWQiLCBjYXB0aW9uID0gIkZpZ3VyZSAxMCIpICsKICBhbm5vdGF0ZSgKICBnZW9tID0gInJlY3QiLAogIHhtaW4gPSAuNSwgeG1heCA9IDExLjUsIHltaW4gPSAuNSwgeW1heCA9IDEuNSwKICBmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSAiI2ZmYjYwMCIsIGFscGhhID0gMC41CiAgCgopCmBgYAoKIyMgKipNT0RFTElORyoqCgpGb3IgdGhlIGJhc2VsaW5lLCB3ZSBpbmNsdWRlZCBjZW5zdXMgdmFyaWFibGVzIGluY2x1ZGluZyBtZWRpYW4gSEggaW5jb21lIChDUEkgaW5mbGF0aW9uIGFkanVzdGVkKSwgTWVkaWFuIGhvbWUgdmFsdWUsIE1lZGlhbiByZW50LCBNZWRpYW4geWVhciBzdHJ1Y3R1cmUgYnVpbHQsIHRlbnVyZSwgbnVtYmVyIG9mIGhvdXNpbmcgdW5pdHMsIG51bWJlciBvZiB2YWNhbmNpZXMsIGNvdW50IG9mIHNlY29uZCBtb3J0Z2FnZXMsIHBlcmNlbnQgd2l0aCBhIEJhY2hlbG9yJ3MgZGVncmVlLCBhbmQgcGVyY2VudCB3aGl0ZSBwb3B1bGF0aW9uLiAKCkFmdGVyIGRvaW5nIG91ciBleHBsb3JhdG9yeSBhbmFseXNpcywgd2UgbmFycm93ZWQgZG93biB0aGUgdmFyaWFibGVzIGZvciBvdXIgbW9kZWwuIEZvciB0aGUgc3BhdGlhbCBhbmQgdGVtcG9yYWwgbGFnIG1vZGVsLCB3ZSB1c2VkIHRoZXNlIHZhcmlhYmxlcywgZ3JvdXBlZCBpbnRvIHRoZSBmb2xsb3dpbmcgY2F0ZWdvcmllczoKCioqVElNRS9TUEFDRSoqIDxicj4KLSBuYW1lIChuZWlnaGJvcmhvb2QpIDxicj4KLSB5ZWFyIDxicj4KCioqR0VOVFJJRklDQVRJT04gSU5ESUNBVE9SUyoqIDxicj4KLSBpbmRpY2F0b3IgKGluY29ycG9yYXRlcyBiYWNoZWxvcnMgZGVncmVlcywgaW5jb21lIHByaWNlIGNoYW5nZXMsIHJlbnQgY2hhbmdlcywgYW5kIHBvcHVsYXRpb24pIDxicj4KCioqREVNT0dSQVBISUMqKiAKKHRoZSBzdHJvbmdlc3QpIDxicj4KLSBCYWNoX05FVCA8YnI+Ci0gbWlkZGxlX1BlcmNlbnRfT3duZXJfMjAyMiA8YnI+Ci0gbWlkZGxlX1BlcmNlbnRfMm1vcnRfTkVUIDxicj4KCihvdGhlciBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMpCi0gTWVkX1ZhbHVlX05FVCA8YnI+Ci0gUGVyY2VudF8ybW9ydF8yMDIyIDxicj4KLSBQZXJjZW50X2JhY2hfTkVUIDxicj4KLSBSZW50X0luY29tZV9SYXRpb18yMDIyIDxicj4KLSBXaGl0ZV9Qb3BfTkVUIDxicj4KCioqQU1FTklUSUVTKiogPGJyPgotIGNhZmVfY291bnQgPGJyPgotIGpvYl9jb3VudCA8YnI+Ci0gdHJhbnNpdF91bmRlcl8xLjUgPGJyPgoKKipUSU1FIExBRyoqIDxicj4KLSBsYXN0X3llYXJfVE9UQUwgPGJyPgotIE1lZF9WYWx1ZV8yMDE3IDxicj4KLSBUb3RhbF9Vbml0c18yMDE3IDxicj4KLSBWYWNhbnRzXzIwMTcgPGJyPgotIG1pZGRsZV9yZW50XzE3IDxicj4KCioqU1BBVElBTCBMQUcqKiA8YnI+Ci0gTG9jYWwgTW9yYW4ncyBJIDxicj4KLSBubl9MWV9wZXJtaXRzIDxicj4KLSBubl9iYWNoXzIyIDxicj4KLSBubl9pbmNvbWVfMTcgPGJyPgoKU2V2ZXJhbCBpdGVyYXRpb25zIG9mIG1vZGVscyB3ZXJlIGRldmVsb3BlZCBvdmVyIHRoZSBjb3Vyc2Ugb2YgdGhpcyBwcm9qZWN0LCBlYWNoIGF0dGVtcHRpbmcgdG8gaXNvbGF0ZSBhbmQgY29hbGVzY2UgdmFyaWFibGVzIGluIGRpZmZlcmVudCBjb21iaW5hdGlvbnMgdG8gY3JlYXRlIGFzIGFjY3VyYXRlIGEgcHJlZGljdGlvbiBhcyBwb3NzaWJsZS4KCldlIHVzZWQgdGhyZWUgY3Jvc3MtdmFsaWRhdGlvbiBtZXRob2RzOiByYW5kb20gMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uLCBzcGF0aWFsIGNyb3NzLXZhbGlkYXRpb24sIGFuZCB0ZW1wb3JhbCBjcm9zcy12YWxpZGF0aW9uLiBCeSBjcm9zcyB2YWxpZGF0aW5nLCB3ZSBjYW4gZGV0ZWN0IGdlbmVyYWxpemFiaWxpdHkgJiBhY2N1cmFjeSBmb3IgdGhlIG1vZGVsJ3MgcHJlZGljdGlvbnMgYWNyb3NzIHRpbWUgYW5kIHNwYWNlIGluIFBoaWxhZGVscGhpYS4gQ3Jvc3MgdmFsaWRhdGlvbiB3aWxsIGFsc28gaGVscCBkZXRlcm1pbmUgd2hldGhlciBvdXIgbW9kZWwgaXMgb3ZlcmZpdHRpbmcuIFdpdGggb3ZlcnRmaXR0aW5nLCB0aGUgbW9kZWwgaXMgdmVyeSBhY2N1cmF0ZSBidXQgbm90IGdlbmVyYWxpemFibGUgdG8gbmV3IGRhdGFzZXRzIGFzIG5ldyBkYXRhIGJlY29tZXMgYXZhaWxhYmxlIGFuZCBpbnB1dCBpbnRvIHRoZSBtb2RlbC4gCgoKCmBgYHtyfQpzZXQuc2VlZCgxNTIpCgpjZW5zdXNfcGVybWl0c19kYXQgPC0gY2Vuc3VzX3Blcm1pdHMgJT4lCiAgZmlsdGVyKHllYXIgJWluJSBjKDIwMjAsIDIwMjEsIDIwMjIpKSAlPiUKICBtdXRhdGUoY3ZJRCA9IHNhbXBsZShyb3VuZChucm93KC4pIC8gMTE1LjUpLCAKICAgICAgICAgICAgICAgICAgICAgICBzaXplPW5yb3coLiksIHJlcGxhY2UgPSBUUlVFKSkgJT4lCiAgZmlsdGVyKG5hbWUgIT0gIkJZQkVSUlkiICYgbmFtZSAhPSAiV0lTU0FISUNLT05fUEFSSyIgJiBuYW1lICE9ICJHTEVOV09PRCIgJiBuYW1lICE9ICJXSVNURVIiCiAgICAgICAgICYgbmFtZSAhPSAiTUNHVUlSRSIpCmBgYAoKCmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CgojIEJBU0VMSU5FCgpyZWcuYmFzZWxpbmUudmFycyA8LSBjKCJ5ZWFyIiwiaW5kaWNhdG9yIiwibmFtZSIsIlBlcmNlbnRfV2hpdGVfMjAyMiIsIlBlcmNlbnRfT3duZXJfMjAyMiIsICJQZXJjZW50X1ZhY2FudF8yMDIyIiwgIk1lZF9JbmNfMjAyMiIsIk1lZF9WYWx1ZV8yMDIyIiwiTWVkX1JlbnRfMjAyMiIsICJNZWRfU3RydWN0dXJlXzIwMjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90X1VuaXRzXzIwMjIiLCAiVmFjYW50c18yMDIyIiwgIlNlY19Nb3J0XzIwMjIiLCAiUGVyY2VudF9iYWNoXzIwMjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmVudF9JbmNvbWVfUmF0aW9fMjAyMiIsCiAgICAgICAgICAgICAgICAgICAgICAgIkJhY2hfTkVUIiwgIm1pZGRsZV9QZXJjZW50X093bmVyXzIwMjIiLCJtaWRkbGVfUGVyY2VudF8ybW9ydF9ORVQiLAogICAgICAgICAgICAgICAgICAgICAgICJNZWRfVmFsdWVfTkVUIiwiUGVyY2VudF9iYWNoX05FVCIsICAiV2hpdGVfUG9wX05FVCIsCiAgICAgICAgICAgICAgICAgICAgICAiY2FmZV9jb3VudCIsICJqb2JfY291bnQiLCAidHJhbnNpdF91bmRlcl8xLjUiKQoKcmVnLmJhc2VsaW5lLnZhcnMuc3BhY2UgPC0gcmVnLmJhc2VsaW5lLnZhcnNbcmVnLmJhc2VsaW5lLnZhcnMgIT0gIm5hbWUiXQpyZWcuYmFzZWxpbmUudmFycy50aW1lIDwtIHJlZy5iYXNlbGluZS52YXJzW3JlZy5iYXNlbGluZS52YXJzICE9ICJ5ZWFyIl0KCiMgU1BBQ0UgQU5EIFRJTUUgLSB6ZXJvIGluZmxhdGVkCgpyZWcuc3QudmFycyA8LSBjKCAieWVhciIsICJpbmRpY2F0b3IiLCAibmFtZSIsIkJhY2hfTkVUIiwibWlkZGxlX1BlcmNlbnRfT3duZXJfMjAyMiIsIm1pZGRsZV9QZXJjZW50XzJtb3J0X05FVCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1lZF9WYWx1ZV9ORVQiLCAiUGVyY2VudF8ybW9ydF8yMDIyIiwgIlBlcmNlbnRfYmFjaF9ORVQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmVudF9JbmNvbWVfUmF0aW9fMjAyMiIsICJXaGl0ZV9Qb3BfTkVUIiwgImNhZmVfY291bnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHJhbnNpdF91bmRlcl8xLjUiLCAgImxhc3RfeWVhcl9UT1RBTCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZWRfVmFsdWVfMjAxNyIsICJUb3RfVW5pdHNfMjAxNyIsICJWYWNhbnRzXzIwMTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibWlkZGxlX3JlbnRfMTciLCAgIkxvY2FsX01vcmFuc19JIiwgICJubl9MWV9wZXJtaXRzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5uX2JhY2hfMjIiLCAibm5faW5jb21lXzE3IiwgImpvYnMubm4iKQoKcmVnLnN0LnNwYWNlIDwtcmVnLnN0LnZhcnNbcmVnLnN0LnZhcnMgIT0gIm5hbWUiXSAKcmVnLnN0LnRpbWUgPC0gcmVnLnN0LnZhcnNbcmVnLnN0LnZhcnMgIT0gInllYXIiXQoKIyBCYXNlbGluZQpyZWcuYmFzZWxpbmUucmFuZG9tIDwtIGNyb3NzVmFsaWRhdGUoCiAgZGF0YXNldCA9IGNlbnN1c19wZXJtaXRzX2RhdCwKICBpZCA9ICJjdklEIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAKICBkZXBlbmRlbnRWYXJpYWJsZSA9ICJUT1RBTCIsCiAgaW5kVmFyaWFibGVzID0gcmVnLmJhc2VsaW5lLnZhcnMpICU+JQogICAgZHBseXI6OnNlbGVjdChjdklEID0gY3ZJRCwgVE9UQUwsIFByZWRpY3Rpb24sIG5hbWUpCgpyZWcuYmFzZWxpbmUuY3YudGltZSA8LSBjcm9zc1ZhbGlkYXRlKAogIGRhdGFzZXQgPSBjZW5zdXNfcGVybWl0c19kYXQsCiAgaWQgPSAieWVhciIsICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgZGVwZW5kZW50VmFyaWFibGUgPSAiVE9UQUwiLAogIGluZFZhcmlhYmxlcyA9IHJlZy5iYXNlbGluZS52YXJzLnRpbWUpICU+JQogICAgZHBseXI6OnNlbGVjdChjdklEID0geWVhciwgVE9UQUwsIFByZWRpY3Rpb24sIG5hbWUpCgpyZWcuYmFzZWxpbmUuY3Yuc3BhY2UgPC0gY3Jvc3NWYWxpZGF0ZSgKICBkYXRhc2V0ID0gY2Vuc3VzX3Blcm1pdHNfZGF0LAogIGlkID0gIm5hbWUiLCAgICAgICAgICAgICAgICAgICAgICAgICAgIAogIGRlcGVuZGVudFZhcmlhYmxlID0gIlRPVEFMIiwKICBpbmRWYXJpYWJsZXMgPSByZWcuYmFzZWxpbmUudmFycy5zcGFjZSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGN2SUQgPSBuYW1lLCBUT1RBTCwgUHJlZGljdGlvbikgJT4lCiAgbXV0YXRlKG5hbWUgPSBjdklEKSAlPiUKICBkcGx5cjo6c2VsZWN0KGN2SUQsIFRPVEFMLCBQcmVkaWN0aW9uLCBuYW1lLCBnZW9tZXRyeSkKCiMgc3BhY2UvdGltZSBsYWcKCnJlZy5zdC5jdi5yYW5kb20gPC0gY3Jvc3NWYWxpZGF0ZSgKICBkYXRhc2V0ID0gY2Vuc3VzX3Blcm1pdHNfZGF0LAogIGlkID0gImN2SUQiLCAgICAgICAgICAgICAgICAgICAgICAgICAgIAogIGRlcGVuZGVudFZhcmlhYmxlID0gIlRPVEFMIiwKICBpbmRWYXJpYWJsZXMgPSByZWcuc3QudmFycykgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGN2SUQgPSBjdklELCBUT1RBTCwgUHJlZGljdGlvbiwgbmFtZSkKCnJlZy5zdC5jdi50aW1lIDwtIGNyb3NzVmFsaWRhdGUoCiAgZGF0YXNldCA9IGNlbnN1c19wZXJtaXRzX2RhdCwKICBpZCA9ICJ5ZWFyIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAKICBkZXBlbmRlbnRWYXJpYWJsZSA9ICJUT1RBTCIsCiAgaW5kVmFyaWFibGVzID0gcmVnLnN0LnRpbWUpICU+JQogICAgZHBseXI6OnNlbGVjdChjdklEID0geWVhciwgVE9UQUwsIFByZWRpY3Rpb24sIG5hbWUpCgpyZWcuc3QuY3Yuc3BhY2UgPC0gY3Jvc3NWYWxpZGF0ZSgKICBkYXRhc2V0ID0gY2Vuc3VzX3Blcm1pdHNfZGF0LAogIGlkID0gIm5hbWUiLCAgICAgICAgICAgICAgICAgICAgICAgICAgIAogIGRlcGVuZGVudFZhcmlhYmxlID0gIlRPVEFMIiwKICBpbmRWYXJpYWJsZXMgPSByZWcuc3Quc3BhY2UpICU+JQogICAgZHBseXI6OnNlbGVjdChjdklEID0gbmFtZSwgVE9UQUwsIFByZWRpY3Rpb24pICU+JQogIG11dGF0ZShuYW1lID0gY3ZJRCkgJT4lCiAgZHBseXI6OnNlbGVjdChjdklELCBUT1RBTCwgUHJlZGljdGlvbiwgbmFtZSwgZ2VvbWV0cnkpCgpyZWcuc3VtbWFyeSA8LSAKICByYmluZCgKICAgIG11dGF0ZShyZWcuYmFzZWxpbmUucmFuZG9tLCAgICAgICAgICAgRXJyb3IgPSBQcmVkaWN0aW9uIC0gVE9UQUwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVncmVzc2lvbiA9ICJSYW5kb20gay1mb2xkIENWOiBCYXNlbGluZSBWYXJpYWJsZXMiKSwKICAgIAogICAgbXV0YXRlKHJlZy5zdC5jdi5yYW5kb20sICAgICAgICAgICBFcnJvciA9IFByZWRpY3Rpb24gLSBUT1RBTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWdyZXNzaW9uID0gIlJhbmRvbSBrLWZvbGQgQ1Y6IFNwYXRpYWwgYW5kIFRlbXBvcmFsIExhZyIpLAogICAgCiAgICBtdXRhdGUocmVnLmJhc2VsaW5lLmN2LnNwYWNlLCAgICBFcnJvciA9IFByZWRpY3Rpb24gLSBUT1RBTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWdyZXNzaW9uID0gIlNwYXRpYWwgTE9HTy1DVjogQmFzZWxpbmUgVmFyaWFibGVzIiksCiAgICAKICAgIG11dGF0ZShyZWcuc3QuY3Yuc3BhY2UsICAgIEVycm9yID0gUHJlZGljdGlvbiAtIFRPVEFMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlZ3Jlc3Npb24gPSAiU3BhdGlhbCBMT0dPLUNWOiBTcGF0aWFsIGFuZCBUZW1wb3JhbCBMYWciKSwKICAgIAogICAgbXV0YXRlKHJlZy5iYXNlbGluZS5jdi50aW1lLCAgICBFcnJvciA9IFByZWRpY3Rpb24gLSBUT1RBTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWdyZXNzaW9uID0gIlRlbXBvcmFsIExPR08tQ1Y6IEJhc2VsaW5lIFZhcmlhYmxlcyIpLAogICAgCiAgICBtdXRhdGUocmVnLnN0LmN2LnRpbWUsICAgIEVycm9yID0gUHJlZGljdGlvbiAtIFRPVEFMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlZ3Jlc3Npb24gPSAiVGVtcG9yYWwgTE9HTy1DVjogU3BhdGlhbCBhbmQgVGVtcG9yYWwgTGFnIikKICAgIAogICAgKSAlPiUKICAgIHN0X3NmKCkgCgpgYGAKCiMjIyBFeHBsb3JlIEVycm9ycwoKCk1lYW4gYWJzb2x1dGUgZXJyb3IgaXMgcXVpdGUgbG93IGFuZCBzaW1pbGFyIGFjcm9zcyBhbGwgY3Jvc3MgdmFsaWRhdGlvbiBtZXRob2RzIHdpdGggb25seSBhIGZldyBvdXRsaWVycy4gVGhlc2Ugb3V0bGllcnMgaGF2ZSBzaWduaWZpY2FudCBjb3VudCBkaWZmZXJlbmNlcywgYnV0IHRoZSByZXN0IG9mIHRoZSBuZWlnaGJvcmhvb2RzIGFyZSBvdmVyYWwgdGhlIHNhbWUuIFRoZSBzcGF0aWFsIGFuZCB0ZW1wb3JhbCBsYWcgbW9kZWwgd2hlbiBjcm9zcyB2YWxpZGF0ZWQgYXQgZmlyc3QgYXBwZWFycyB0byBiZSB0aGUgc3Ryb25nZXN0IG1vZGVsIHdpdGggdGhlIGxvd2VzdCBlcnJvciwgd2hpY2ggc3VnZ2VzdHMgdGhhdCB0aW1lIGFuZCBzcGFjZSBsYWcgZmFjdG9ycyBhcmUgdGhlIG1vc3QgaW5mbHVlbnRpYWwgcHJlZGljdG9ycy4gCgoKYGBge3J9CmVycm9yX2J5X3JlZyA8LSAKICByZWcuc3VtbWFyeSAlPiUKICAgIGdyb3VwX2J5KFJlZ3Jlc3Npb24pICU+JSAKICAgIHN1bW1hcml6ZShNZWFuX0Vycm9yID0gbWVhbihFcnJvciwgbmEucm0gPSBUKSwKICAgICAgICAgICAgICBNQUUgPSBtZWFuKGFicyhFcnJvciksIG5hLnJtID0gVCksCiAgICAgICAgICAgICAgU0RfTUFFID0gc2QoYWJzKEVycm9yKSwgbmEucm0gPSBUKSkgCgplcnJvcl9ieV9yZWdfbmggPC0gCiAgcmVnLnN1bW1hcnkgJT4lCiAgICBncm91cF9ieShSZWdyZXNzaW9uLCBuYW1lKSAlPiUgCiAgICBzdW1tYXJpemUoTWVhbl9FcnJvciA9IG1lYW4oUHJlZGljdGlvbiAtIFRPVEFMLCBuYS5ybSA9IFQpLAogICAgICAgICAgICAgIE1BRSA9IG1lYW4oYWJzKFByZWRpY3Rpb24gLSBUT1RBTCksIG5hLnJtID0gVCksCiAgICAgICAgICAgICAgU0RfTUFFID0gc2QoYWJzKFByZWRpY3Rpb24gLSBUT1RBTCksIG5hLnJtID0gVCkpICU+JQogIHVuZ3JvdXAoKQoKIyMgcGxvdCBoaXN0b2dyYW0gb2YgZXJyb3JzCmVycm9yX2J5X3JlZ19uaCAlPiUKICBnZ3Bsb3QoYWVzKE1BRSkpICsgCiAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsIGNvbG91cj0iYmxhY2siLCBmaWxsID0gIiNmMjAwODkiKSArCiAgZmFjZXRfd3JhcCh+UmVncmVzc2lvbiwgbmNvbCA9IDIpICsKICAgIGxhYnModGl0bGU9IkRpc3RyaWJ1dGlvbiBvZiBNQUUgYnkgUmVncmVzc2lvbiBhbmQgTmVpZ2hib3Job29kIiwKICAgICAgICAgeD0iTWVhbiBBYnNvbHV0ZSBFcnJvciIsIHk9IkNvdW50IikKCgpgYGAKCiMjIyMgU3BhdGlhbCBEaXN0cmlidXRpb24gb2YgRXJyb3JzCgpXaGVuIHdlIGV4YW1pbmUgdGhlIHRlbXBvcmFsIGFuZCBzcGF0aWFsIGxhZyBpbiBmdXJ0aGVyIGRldGFpbCwgaXQgYmVjb21lcyBhcHBhcmVudCB0aGF0IHRoZSBuZWlnaGJvcmhvb2RzIHdpdGggaGlnaGVzdCBvdXRsaWVyIGluIHByZWRpY3RlZCBjb3VudCBhcmUgbmVhciB0aGUgRmlzaHRvd24gYW5kIFNvdXRod2VzdCBQaGlsbHkgbmVpZ2hib3Job29kcy4gT3RoZXIgdGhhbiB0aGVzZSwgZXJyb3IgaXMgcXVpdGUgc21hbGwgdGhyb3VnaG91dCB0aGUgcmVzdCBvZiBQaGlsYWRlbHBoaWEuIAoKYGBge3J9CgpyZWcuc3VtbWFyeSAlPiUKICBmaWx0ZXIoUmVncmVzc2lvbiA9PSAiVGVtcG9yYWwgTE9HTy1DVjogU3BhdGlhbCBhbmQgVGVtcG9yYWwgTGFnIikgJT4lCiAgZ3JvdXBfYnkobmFtZSkgJT4lCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQogIHN1bW1hcml6ZShyZXNpZHVhbHMgPSBtZWFuKEVycm9yLCBuYS5ybSA9IFQpKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIGxlZnRfam9pbihwaGwubmgsIGJ5ID0gYygibmFtZSIgPSAibmFtZSIpKSAlPiUKICAgIHN0X3NmKCkgJT4lCiAgICBnZ3Bsb3QoKSArIAogICAgICBnZW9tX3NmKGFlcyhmaWxsID0gcmVzaWR1YWxzKSkgKwogICAgICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAiIzg5MDBmMiIgLCBtaWQgPSAgIiNmZmI2MDAiLCBoaWdoID0gIiNlNTAwYTQiLCBuYW1lID0gIkVycm9yIikgKwogICAgICBsYWJzKHRpdGxlID0gIlNwYXRpYWwgYW5kIFRlbXBvcmFsIExhZzogXG5NZWFuIEFic29sdXRlIEVycm9yIGJ5IE5laWdoYm9yaG9vZCIsIGNhcHRpb24gPSAiRmlndXJlIDEwIikgKwogICAgICBtYXBUaGVtZSgpCgoKYGBgCgpGaW5hbGx5LCB3aGVuIHBsb3R0aW5nIHByZWRpY3RlZCBwZXJtaXRzIGNvbXBhcmVkIHdpdGggb2JzZXJ2ZWQgcGVybWl0cywgd2UgY2FuIHNlZSB0aGF0IHRoZSBvYnNlcnZlZCBhbmQgcHJlZGljdGVkIHBlcm1pdHMgYXJlIGZhaXJseSBjbG9zZSBidXQgbm90IHNvIG11Y2ggdGhhdCB0aGUgbW9kZWwgaXMgb3ZlcmZpdHRpbmcuIEl0IGlzIHRoZXJlZm9yZSBnZW5lcmFsaXphYmxlIGFuZCBvdmVyYWxsIGFjY3VyYXRlLiAKCmBgYHtyfQoKcmVnLnN1bW1hcnkgJT4lCiAgZmlsdGVyKFJlZ3Jlc3Npb24gPT0gIlRlbXBvcmFsIExPR08tQ1Y6IFNwYXRpYWwgYW5kIFRlbXBvcmFsIExhZyIpICU+JSAgCiAgZ2dwbG90KGFlcyhUT1RBTCwgUHJlZGljdGlvbikpICsKICBnZW9tX3BvaW50KCkgKwogIHN0YXRfc21vb3RoKGFlcyhUT1RBTCwgVE9UQUwpLCAKICAgICAgICAgICAgIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIHNpemUgPSAxLCBjb2xvdXI9IiNmZmI2MDAiKSArIAogIHN0YXRfc21vb3RoKGFlcyhQcmVkaWN0aW9uLCBUT1RBTCksIAogICAgICAgICAgICAgIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIHNpemUgPSAxLCBjb2xvdXI9IiNmMjAwODkiKSArCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIHBlcm1pdHMgYXMgYSBmdW5jdGlvbiBvZiBvYnNlcnZlZCBwZXJtaXRzIiwKICAgICAgIHN1YnRpdGxlPSJPcmFuZ2UgbGluZSByZXByZXNlbnRzIGEgcGVyZmVjdCBwcmVkaWN0aW9uOyBQaW5rIGxpbmUgcmVwcmVzZW50cyBwcmVkaWN0aW9uIiwgY2FwdGlvbiA9ICJGaWd1cmUgMjEiKSArCiAgeGxhYigiQWN0dWFsIFBlcm1pdHMiKSArCiAgeWxhYigiUHJlZGljdGVkIFBlcm1pdHMiKSArCiAgcGxvdFRoZW1lKCkKCgoKYGBgCgpPdXIgc3BhdGlhbCByZWdyZXNzaW9uIG1vZGVsIGNyb3NzIHZhbGlkYXRlcyBvbiBhIG5laWdoYm9yaG9vZCBsZXZlbCBhY3Jvc3MgdGltZSwgd2hpbGUgb3VyIHRlbXBvcmFsIHJlZ3Jlc3Npb24gY3Jvc3MgdmFsaWRhdGVzIHllYXIgYnkgeWVhci4gUmVncmVzc2lvbiBlcnJvciBmb3IgdGhlIHRlbXBvcmFsIGxvZ28gY3Jvc3MgdmFsaWRhdGlvbiB3aXRoIHNwYXRpYWwgYW5kIHRlbXBvcmFsIGxhZyBoYXZlIHRoZSBsb3dlc3QgbWVhbiBhYnNvbHV0ZSBlcnJvciBhcyB3ZWxsIGFzIHN0YW5kYXJkIGRldmlhdGlvbiwgd2hpY2ggcmVtYWlucyBjb25zaXN0ZW50IHdpdGggb3VyIG90aGVyIGZpbmRpbmdzIG9mIHRoYXQgbW9kZWwuIEl0IGlzIGNsZWFyIHRoYXQgdGhpcyBtb2RlbCBpcyBnZW5lcmFsaXphYmxlLCBhY2N1cmF0ZSwgYW5kIG92ZXJhbGwgYSBnb29kIGZpdCBhY3Jvc3MgbWVhc3VyZW1lbnRzLiBCZWNhdXNlIGNyb3NzIHZhbGlkYXRpbmcgb24gYSB0ZW1wb3JhbCBzY2FsZSBhcHBlYXJzIHRvIGhhdmUgdGhlIGxvd2VzdCBtZWFuIE1BRSBhbmQgU3RhbmRhcmQgRGV2aWF0aW9uLCB3ZSBjYW4gYmUgZmFpcmx5IGNlcnRhaW4gdGhhdCB0aW1lIGlzIGtleSB0byBwcmVkaWN0aW5nIHBlcm1pdCBpc3N1YW5jZS4gVGhpcyBpcyBhbHNvIGltcG9ydGFudCBzaW5jZSBpdCBtZWFucyB0aGF0IGFkZGl0aW9uYWwgeWVhcnMgb2YgZGF0YSB3aWxsIGlkZWFsbHkgYmUgYWJsZSB0byBwcmVkaWN0IHBlcm1pdCBpc3N1YW5jZSB3aXRoIGZ1dHVyZSB5ZWFycyBvZiBkYXRhc2V0cy4gCgoKYGBge3J9CnN0X2Ryb3BfZ2VvbWV0cnkoZXJyb3JfYnlfcmVnX25oKSAlPiUKICBncm91cF9ieShSZWdyZXNzaW9uKSAlPiUgCiAgICBzdW1tYXJpemUoTWVhbl9NQUUgPSByb3VuZChtZWFuKE1BRSksIDIpLAogICAgICAgICAgICAgIFNEX01BRSA9IHJvdW5kKHNkKE1BRSksIDIpKSAlPiUKICBrYWJsZSgpICU+JQogICAga2FibGVfc3R5bGluZygic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGKSAlPiUKICAgIHJvd19zcGVjKDMsIGNvbG9yID0gImJsYWNrIiwgYmFja2dyb3VuZCA9ICIjODkwMGYyIikgJT4lCiAgICByb3dfc3BlYyg0LCBjb2xvciA9ICJibGFjayIsIGJhY2tncm91bmQgPSAiIzg5MDBmMiIpICU+JQogICAgcm93X3NwZWMoNSwgY29sb3IgPSAiYmxhY2siLCBiYWNrZ3JvdW5kID0gIiNmZmI2MDAiKSAlPiUKICAgIHJvd19zcGVjKDYsIGNvbG9yID0gImJsYWNrIiwgYmFja2dyb3VuZCA9ICIjZmZiNjAwIikKCgoKYGBgCgojIyAqKkNPTkNMVVNJT04qKgoKIyMjIERpc2N1c3Npb24KCk92ZXJhbGwsIHRoaXMgbW9kZWwgaXMgZWZmZWN0aXZlIGFuZCB3aGVuIGNvdXBsZWQgd2l0aCBpbmRpY2F0b3JzIG9mIGdlbnRyaWZpY2F0aW9uLCBjYW4gaGVscCBwcmVkaWN0IHdoaWNoIG5laWdoYm9yaG9vZHMgYXJlIG1vc3QgbGlrZWx5IGFyZWFzIHdpdGggaGlnaCBhbW91bnRzIG9mIGRldmVsb3BtZW50IGFuZCBleHBlcmllbmNpbmcgaW50ZW5zZSBuZWlnaGJvcmhvb2QgY2hhbmdlLiBUbyBwcmV2ZW50IGRpc3BsYWNlbWVudCBmb3IgbG9uZy10ZXJtIGFuZCBzb2Npb2Vjb25vbWljYWxseSBkaXNhZHZhbnRhZ2VkIHJlc2lkZW50cywgY29tbXVuaXR5IGxhbmQgdHJ1c3RzIGFuZCBob3VzaW5nIHRydXN0IGZ1bmRzIGNhbiBmdW5kIGxvbmctdGVybSBzb2x1dGlvbnMuIFNpbmNlIGl0IGlzIHRpbWUtY29uc3VtaW5nIHRvIGRldmVsb3AsICoqZnVuZCoqLCBhbmQgaW1wbGVtZW50IHRoZXNlIHR5cGVzIG9mIHNvbHV0aW9ucywgYW50aWNpcGF0aW5nIGEgZmV3IHllYXJzIG91dCBjYW4gcHJldmVudCBkaXNwbGFjZW1lbnQgYW5kIGNyZWF0ZSBsb25nLXRlcm0gYWZmb3JkYWJsZSBob3VzaW5nLiBUYXJnZXRpbmcgYXJlYXMgdGhhdCBhcmUgZXhwZXJpZW5jaW5nIGVhcmx5IGluZGljYXRvcnMgb2YgZ2VudHJpZmljYXRpb24gYnV0IG5vdCBoaWdoIGFtb3VudHMgb2YgcGVybWl0IGRldmVsb3BtZW50IGlzIGEgY3JpdGljYWwgc3RyYXRlZ3kuIAoKIyMjIFVzZSBDYXNlIEFwcGxpY2F0aW9uCgoKSWRlbnRpZnlpbmcgYXJhcyBmb3IgY29tbXVuaXR5IGxhbmQgdHJ1c3QgaW52ZXN0bWVudCBjYW4gaGVscCBhcyBhIGxvbmcgdGVybSBzdHJhdGVneSBmb3IgYW50aS1kaXNwbGFjZW1lbnQsIGxpa2Ugem9uaW5nLiBbQ29tbXVuaXR5IGxhbmQgdHJ1c3RzXShodHRwczovL2dyb3VuZGVkc29sdXRpb25zLm9yZy9zdHJlbmd0aGVuaW5nLW5laWdoYm9yaG9vZHMvY29tbXVuaXR5LWxhbmQtdHJ1c3RzKSB3aWxsIGJlIGluZmx1ZW50aWFsIGluIHByZXZlbnRpbmcgUGhpbGFkZWxwaGlhIGZyb20gcHVzaGluZyBvdXQgaXRzIGxvbmctdGVybSByZXNpZGVudHMuIApJZGVudGlmeWluZyBhcmVhcyBmb3IgY29tbXVuaXR5IGxhbmQgdHJ1c3QgaW52ZXN0bWVudCBjYW4gaGVscCBhcyBhIGxvbmcgdGVybSBzdHJhdGVneSB0byBmaWdodCBkaXNwbGFjZW1lbnQuIFdlIGhvcGUgdGhhdCB0aGlzIGNhbiBoZWxwIHNlY3VyZSBhZmZvcmRhYmxlIGhvdXNpbmcgYW5kIHByZWRpY3QgdGhlIGJlc3QgYXJlYXMgdG8gc2VjdXJlIGl0IGZvciB0aGUgZnV0dXJlLiAKCkZvciBhIGJyaWVmIHByZXNlbnRhdGlvbiBvZiB0aGlzIG1hcmtkb3duLCBwbGVhc2UgdmlzaXQgb3VyIFt2aWRlbyBoZXJlXSgpCg==